#
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(span) = source.frontmatter_span() {
    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        // Include from file start to frontmatter end when we parse the TOML to get line numbers
    26        // correct and so if a TOML error says "entire file", it shows the existing content, rather
    27        // than blank lines.
    28        //
    29        // HACK: Since frontmatter open isn't valid TOML, we insert a comment
    30        let mut frontmatter = content[0..span.end].to_owned();
    31        let open_span = source.open_span().unwrap();
    32        frontmatter.insert(open_span.start, '#');
    33        Ok(frontmatter)
    34    } else {
    35        // Consider the shebang to be part of the frontmatter
    36        // so if a TOML error says "entire file", it shows the existing content, rather
    37        // than blank lines.
    38        let span = source.shebang_span().unwrap_or(0..0);
    39        Ok(content[span].to_owned())
    40    }
    41}
    42
    43/// Ensure the package name matches the validation from `ops::cargo_new::check_name`
    44pub fn sanitize_name(name: &str) -> String {
    45    let placeholder = if name.contains('_') {
    46        '_'
    47    } else {
    48        // Since embedded manifests only support `[[bin]]`s, prefer arrow-case as that is the
    49        // more common convention for CLIs
    50        '-'
    51    };
    52
    53    let mut name = PackageName::sanitize(name, placeholder).into_inner();
    54
    55    loop {
    56        if restricted_names::is_keyword(&name) {
    57            name.push(placeholder);
    58        } else if restricted_names::is_conflicting_artifact_name(&name) {
    59            // Being an embedded manifest, we always assume it is a `[[bin]]`
    60            name.push(placeholder);
    61        } else if name == "test" {
    62            name.push(placeholder);
    63        } else if restricted_names::is_windows_reserved(&name) {
    64            // Go ahead and be consistent across platforms
    65            name.push(placeholder);
    66        } else {
    67            break;
    68        }
    69    }
    70
    71    name
    72}
    73
    74#[cfg(test)]
    75mod test {
    76    use snapbox::assert_data_eq;
    77    use snapbox::str;
    78
    79    use super::*;
    80
    81    #[track_caller]
    82    fn expand(source: &str) -> String {
    83        expand_manifest(source).unwrap_or_else(|err| panic!("{}", err))
    84    }
    85
    86    #[test]
    87    fn expand_default() {
    88        assert_data_eq!(expand(r#"fn main() {}"#), str![""]);
    89    }
    90
    91    #[test]
    92    fn expand_dependencies() {
    93        assert_data_eq!(
    94            expand(
    95                r#"---cargo
    96[dependencies]
    97time="0.1.25"
    98---
    99fn main() {}
    100"#
    101            ),
    102            str![[r##"
    103#---cargo
    104[dependencies]
    105time="0.1.25"
    106
    107"##]]
    108        );
    109    }
    110}