cargo/util/toml/
embedded.rs

1use cargo_util_schemas::manifest::PackageName;
2
3use crate::CargoResult;
4use crate::util::frontmatter::ScriptSource;
5use crate::util::restricted_names;
6
7pub(super) fn expand_manifest(content: &str) -> CargoResult<String> {
8    let source = ScriptSource::parse(content)?;
9    if let Some(frontmatter) = source.frontmatter() {
10        match source.info() {
11            Some("cargo") | None => {}
12            Some(other) => {
13                if let Some(remainder) = other.strip_prefix("cargo,") {
14                    anyhow::bail!(
15                        "cargo does not support frontmatter infostring attributes like `{remainder}` at this time"
16                    )
17                } else {
18                    anyhow::bail!(
19                        "frontmatter infostring `{other}` is unsupported by cargo; specify `cargo` for embedding a manifest"
20                    )
21                }
22            }
23        }
24
25        Ok(frontmatter.to_owned())
26    } else {
27        let frontmatter = "";
28        Ok(frontmatter.to_owned())
29    }
30}
31
32/// Ensure the package name matches the validation from `ops::cargo_new::check_name`
33pub fn sanitize_name(name: &str) -> String {
34    let placeholder = if name.contains('_') {
35        '_'
36    } else {
37        // Since embedded manifests only support `[[bin]]`s, prefer arrow-case as that is the
38        // more common convention for CLIs
39        '-'
40    };
41
42    let mut name = PackageName::sanitize(name, placeholder).into_inner();
43
44    loop {
45        if restricted_names::is_keyword(&name) {
46            name.push(placeholder);
47        } else if restricted_names::is_conflicting_artifact_name(&name) {
48            // Being an embedded manifest, we always assume it is a `[[bin]]`
49            name.push(placeholder);
50        } else if name == "test" {
51            name.push(placeholder);
52        } else if restricted_names::is_windows_reserved(&name) {
53            // Go ahead and be consistent across platforms
54            name.push(placeholder);
55        } else {
56            break;
57        }
58    }
59
60    name
61}
62
63#[cfg(test)]
64mod test {
65    use snapbox::assert_data_eq;
66    use snapbox::str;
67
68    use super::*;
69
70    #[track_caller]
71    fn expand(source: &str) -> String {
72        expand_manifest(source).unwrap_or_else(|err| panic!("{}", err))
73    }
74
75    #[test]
76    fn expand_default() {
77        assert_data_eq!(expand(r#"fn main() {}"#), str![""]);
78    }
79
80    #[test]
81    fn expand_dependencies() {
82        assert_data_eq!(
83            expand(
84                r#"---cargo
85[dependencies]
86time="0.1.25"
87---
88fn main() {}
89"#
90            ),
91            str![[r#"
92[dependencies]
93time="0.1.25"
94
95"#]]
96        );
97    }
98}