Como um programa Rust lê seus metadados do Cargo.toml
\n\nVocê já escreveu uma CLI-utilitária em Rust e queria que, ao ser executada com a flag --version, ela exibisse não "0.1.0", mas exatamente a versão indicada no Cargo.toml? Ou, talvez, você precisasse registrar o nome do crate e sua descrição — mas puxar essas strings do código era inconveniente e propenso a erros?
Esta pergunta é uma das mais populares no Stack Overflow (milhares de visualizações!). E não é à toa: aparentemente, o que poderia ser mais simples do que ler a própria configuração? Mas em Rust existem armadilhas, e principalmente, várias formas elegantes de resolver.
\n\nNeste artigo, vamos abordar três abordagens principais para obter metadados do pacote Cargo diretamente do código: desde a macro simples env!() até o flexível build.rs. Você aprenderá como ler corretamente a versão, o nome do crate, a descrição e como evitar erros típicos.
Por que isso é necessário? Três cenários típicos
\n\nAntes de mergulhar no código, vamos entender por que acessar os metadados do pacote. Aqui estão três casos reais:
\n\n- \n
- CLI-utilitárias: o comando
myapp --versiondeve exibir a versão doCargo.toml, e não um valor hardcoded. \n - Logs e telemetria: ao iniciar a aplicação, é útil registrar no log o nome do crate e sua versão — isso simplifica a depuração. \n
- Geração automática de documentação: é possível inserir a descrição do crate em mensagens de help ou README gerados dinamicamente. \n
Agora que entendemos o "porquê", vamos ao "como".
\n\nMétodo nº1: Macro env!() — o mais simples e popular
\n\nO caminho mais direto é usar a macro embutida env!(). Ela permite ler variáveis de ambiente que o Cargo define automaticamente durante a compilação. Aqui estão as mais úteis:
- \n
CARGO_PKG_NAME— nome do pacote (ex.: "my_cool_app") \n CARGO_PKG_VERSION— versão (ex.: "0.2.1") \n CARGO_PKG_DESCRIPTION— descrição do campodescriptionnoCargo.toml\n CARGO_PKG_AUTHORS— lista de autores separada por dois-pontos \n CARGO_PKG_LICENSE— licença \n CARGO_PKG_REPOSITORY— URL do repositório \n
Exemplo de uso:
\n\nfn main() {\n let name = env!("CARGO_PKG_NAME");\n let version = env!("CARGO_PKG_VERSION");\n\n println!("{} v{}", name, version);\n // Saída: my_cool_app v0.2.1\n}\n\n\nImportante: env!() é uma macro de tempo de compilação. Se a variável de ambiente não estiver definida, o programa nem compila! Isso é um ponto positivo (segurança de tipos) e negativo (não é possível fazer fallback para hardcode sem truques adicionais).
Se você precisar da versão apenas para depuração, pode usar option_env!(), que retorna Option<&str>:
let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");\nprintln!("Version: {}", version);\n\n\nEste método é ideal para 90% dos casos. Não requer dependências adicionais e funciona "de fábrica".
\n\nMétodo nº2: include_str!() + parsing manual — para máximo controle
\n\nÀs vezes, você não precisa apenas ler um campo, mas obter dados estruturados do Cargo.toml. Por exemplo, todas as dependências ou palavras-chave. Nesse caso, você pode ler o próprio arquivo como string e fazer o parsing.
fn main() {\n let cargo_toml = include_str!("../Cargo.toml");\n // Aqui você pode usar um parser toml, mas para exemplo — busca simples\n if let Some(line) = cargo_toml.lines().find(|l| l.starts_with("version =")) {\n let version = line.trim_start_matches("version = \\"").trim_end_matches('"');\n println!("Version from file: {}", versio