//! Common utilities pub(crate) mod starlark; pub(crate) mod symlink; pub(crate) mod target_triple; pub(crate) const CRATES_IO_INDEX_URL: &str = "https://github.com/rust-lang/crates.io-index"; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; /// Convert a string into a valid crate module name by applying transforms to invalid characters pub(crate) fn sanitize_module_name(name: &str) -> String { name.replace('-', "_") } /// Some character which may be present in version IDs are not valid /// in Bazel repository names. This converts invalid characters. See /// [RepositoryName.java](https://github.com/bazelbuild/bazel/blob/4.0.0/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryName.java#L42) pub(crate) fn sanitize_repository_name(name: &str) -> String { name.replace('+', "-") } /// Vendored crates are generated by cargo itself in `src/metadata.rs` in the /// `VendorGenerator::generate()` method. This means that the semver metadata will /// always contain a (+) symbol, which is not compatible with bazel's labels. /// This function will rename the cargo vendor generated file paths to be compatible with bazel /// labels by simply replacing the (+) with a (-). If this file is called by any other cli mod, /// it just simply joins the out dir to the path pub(crate) fn normalize_cargo_file_paths( outputs: BTreeMap, out_dir: &Path, ) -> BTreeMap { outputs .into_iter() .map(|(path, content)| { let path = out_dir.join(path); // Get Path Str and Parent Path Str so we can rename the root file let original_path_str = path.to_str().expect("All file paths should be strings"); let original_parent_path_str = path .parent() .expect("Should have parent") .to_str() .expect("All file paths should be strings"); let new_path = if original_parent_path_str.contains('+') { let new_parent_file_path = sanitize_repository_name(original_parent_path_str); std::fs::rename(original_parent_path_str, new_parent_file_path) .expect("Could not rename paths"); PathBuf::from(&original_path_str.replace('+', "-")) } else { path }; (new_path, content) }) .collect() } #[cfg(test)] mod test { use super::*; #[test] fn test_sanitize_repository_name() { let name = "anyhow-1.0.0+semver_meta"; let got = sanitize_repository_name(name); assert_eq!(got, String::from("anyhow-1.0.0-semver_meta")); } #[test] fn test_sanitize_repository_name_no_change() { let name = "tokio-1.20.0"; let got = sanitize_repository_name(name); assert_eq!(got, String::from("tokio-1.20.0")); } #[test] fn test_normalize_cargo_file_paths() { let mut outputs = BTreeMap::new(); outputs.insert( PathBuf::from("libbpf-sys-1.3.0+v1.3.0/BUILD.bazel"), "contents".into(), ); let outdir = tempfile::tempdir().unwrap(); // create dir to mimic cargo vendor std::fs::create_dir_all(outdir.path().join("libbpf-sys-1.3.0+v1.3.0")).unwrap(); let got = normalize_cargo_file_paths(outputs, outdir.path()); for output in got.into_keys() { assert!(!output.to_str().unwrap().contains('+')); } } #[test] fn test_normalize_cargo_file_paths_no_rename() { let mut outputs = BTreeMap::new(); outputs.insert(PathBuf::from("tokio-1.30.0/BUILD.bazel"), "contents".into()); let outdir = tempfile::tempdir().unwrap(); let got = normalize_cargo_file_paths(outputs, outdir.path()); for output in got.into_keys() { assert!(!output.to_str().unwrap().contains('+')); } } }