use crate::{canon_feature_name, realpath, target};
use std::env;
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
pub mod features {
use std::env;
pub struct Iter {
cursor: env::Vars,
}
impl Iterator for Iter {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
for (key, _) in self.cursor.by_ref() {
if let Some(suffix) = key.strip_prefix("CARGO_FEATURE_") {
return Some(decode(suffix));
}
}
None
}
}
pub fn all() -> Iter {
Iter {
cursor: env::vars(),
}
}
pub fn enabled(name: &str) -> bool {
let key = format!("CARGO_FEATURE_{}", encode(name));
env::var(&key).is_ok()
}
fn decode(name: &str) -> String {
name.chars()
.map(|c| match c {
'A'..='Z' => c.to_ascii_lowercase(),
'_' => '-',
other => other,
})
.collect()
}
fn encode(name: &str) -> String {
name.chars()
.map(|c| match c {
'a'..='z' => c.to_ascii_uppercase(),
'-' => '_',
other => other,
})
.collect()
}
}
pub fn crate_name() -> String {
env::var("CARGO_CRATE_NAME").expect("CARGO_CRATE_NAME env var is not set")
}
pub mod manifest {
use std::env;
use std::path::PathBuf;
pub fn dir() -> PathBuf {
let raw = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR env var is not set");
PathBuf::from(raw)
}
pub fn links() -> Option<String> {
env::var("CARGO_MANIFEST_LINKS").ok()
}
pub fn path() -> PathBuf {
let raw =
env::var("CARGO_MANIFEST_PATH").expect("CARGO_MANIFEST_PATH env var is not set");
PathBuf::from(raw)
}
}
pub mod pkg {
use std::env;
pub fn authors() -> Vec<String> {
let raw = env::var("CARGO_PKG_AUTHORS").expect("CARGO_PKG_AUTHORS env var is not set");
raw.split(':').map(ToOwned::to_owned).collect()
}
pub fn description() -> Option<String> {
let raw =
env::var("CARGO_PKG_DESCRIPTION").expect("CARGO_PKG_DESCRIPTION env var is not set");
if raw.is_empty() {
None
} else {
Some(raw)
}
}
pub fn homepage() -> Option<String> {
let raw = env::var("CARGO_PKG_HOMEPAGE").expect("CARGO_PKG_HOMEPAGE env var is not set");
if raw.is_empty() {
None
} else {
Some(raw)
}
}
pub fn name() -> String {
env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME env var is not set")
}
pub fn repository() -> Option<String> {
let raw = env::var("CARGO_PKG_REPOSITORY").expect("CARGO_PKG_REPOSITORY env var is not set");
if raw.is_empty() {
None
} else {
Some(raw)
}
}
pub fn license() -> Option<String> {
let raw = env::var("CARGO_PKG_LICENSE").ok()?;
if raw.is_empty() {
None
} else {
Some(raw)
}
}
pub fn rust_version() -> Option<String> {
let raw = env::var("CARGO_PKG_RUST_VERSION").ok()?;
if raw.is_empty() {
None
} else {
Some(raw)
}
}
pub fn version_major() -> u64 {
let raw =
env::var("CARGO_PKG_VERSION_MAJOR").expect("CARGO_PKG_VERSION_MAJOR env var is not set");
raw.parse().expect("CARGO_PKG_VERSION_MAJOR is not a valid integer")
}
pub fn version_minor() -> u64 {
let raw =
env::var("CARGO_PKG_VERSION_MINOR").expect("CARGO_PKG_VERSION_MINOR env var is not set");
raw.parse().expect("CARGO_PKG_VERSION_MINOR is not a valid integer")
}
pub fn version_patch() -> u64 {
let raw =
env::var("CARGO_PKG_VERSION_PATCH").expect("CARGO_PKG_VERSION_PATCH env var is not set");
raw.parse().expect("CARGO_PKG_VERSION_PATCH is not a valid integer")
}
pub fn version_pre() -> String {
env::var("CARGO_PKG_VERSION_PRE").expect("CARGO_PKG_VERSION_PRE env var is not set")
}
}
pub fn triple_with_linker() -> (String, Option<String>) {
let triple = target::triple().to_string();
if let Ok(linker_path) = env::var(format!(
"CARGO_TARGET_{}_LINKER",
canon_feature_name(&triple)
))
.or_else(|_| env::var("RUSTC_LINKER"))
{
let linker = Path::new(&linker_path)
.file_stem()
.unwrap()
.to_string_lossy()
.into_owned();
if let Some(prefix) = linker
.strip_suffix("-gcc")
.or_else(|| linker.strip_suffix("-cc"))
.filter(|p| p.chars().filter(|&x| x == '-').count() >= 2)
{
return (prefix.to_owned(), Some(linker));
}
(triple, Some(linker))
} else {
(triple, None)
}
}
struct BuildDirs {
workspace_dir: PathBuf,
target_dir: PathBuf,
output_dir: PathBuf,
}
static BUILD_DIRS: LazyLock<BuildDirs> = LazyLock::new(|| {
let out_path = realpath(env::var("OUT_DIR").unwrap());
let mut it = out_path.ancestors().skip(3);
let output_dir = it.next().unwrap().to_owned();
let mut t = it.next().unwrap().to_owned();
let mut w = it.next().unwrap().to_owned();
while !w.join("Cargo.toml").is_file() {
t = w.clone();
w = it.next().unwrap().to_owned();
}
BuildDirs {
workspace_dir: w,
target_dir: t,
output_dir,
}
});
pub fn workspace_dir() -> PathBuf {
BUILD_DIRS.workspace_dir.clone()
}
pub fn build_target_dir() -> PathBuf {
BUILD_DIRS.target_dir.clone()
}
pub fn build_output_dir() -> PathBuf {
BUILD_DIRS.output_dir.clone()
}