#
cargo/util/toml/
embedded.rs

    1use cargo_util_schemas::manifest::PackageName;
    2
    3use crate::util::frontmatter::FrontmatterError;
    4use crate::util::frontmatter::ScriptSource;
    5
    6pub(super) fn expand_manifest(content: &str) -> Result<String, FrontmatterError> {
    7    let source = ScriptSource::parse(content)?;
    8    if let Some(span) = source.frontmatter_span() {
    9        match source.info() {
    10            Some("cargo") | None => {}
    11            Some(other) => {
    12                let info_span = source.info_span().unwrap();
    13                let close_span = source.close_span().unwrap();
    14                if let Some(remainder) = other.strip_prefix("cargo,") {
    15                    return Err(FrontmatterError::new(
    16                        format!("unsupported frontmatter infostring attributes: `{remainder}`"),
    17                        info_span,
    18                    )
    19                    .push_visible_span(close_span));
    20                } else {
    21                    return Err(FrontmatterError::new(
    22                        format!(
    23                            "unsupported frontmatter infostring `{other}`; specify `cargo` for embedding a manifest"
    24                        ),
    25                        info_span,
    26                    ).push_visible_span(close_span));
    27                }
    28            }
    29        }
    30
    31        // Include from file start to frontmatter end when we parse the TOML to get line numbers
    32        // correct and so if a TOML error says "entire file", it shows the existing content, rather
    33        // than blank lines.
    34        //
    35        // HACK: Since frontmatter open isn't valid TOML, we insert a comment
    36        let mut frontmatter = content[0..span.end].to_owned();
    37        let open_span = source.open_span().unwrap();
    38        frontmatter.insert(open_span.start, '#');
    39        Ok(frontmatter)
    40    } else {
    41        // Consider the shebang to be part of the frontmatter
    42        // so if a TOML error says "entire file", it shows the existing content, rather
    43        // than blank lines.
    44        let span = source.shebang_span().unwrap_or(0..0);
    45        Ok(content[span].to_owned())
    46    }
    47}
    48
    49/// Ensure the package name matches the validation from `ops::cargo_new::check_name`
    50pub fn sanitize_name(name: &str) -> String {
    51    let placeholder = if name.contains('_') {
    52        '_'
    53    } else {
    54        // Since embedded manifests only support `[[bin]]`s, prefer arrow-case as that is the
    55        // more common convention for CLIs
    56        '-'
    57    };
    58
    59    PackageName::sanitize(name, placeholder).into_inner()
    60}
    61
    62#[cfg(test)]
    63mod test {
    64    use snapbox::assert_data_eq;
    65    use snapbox::str;
    66
    67    use super::*;
    68
    69    #[track_caller]
    70    fn expand(source: &str) -> String {
    71        expand_manifest(source).unwrap_or_else(|err| panic!("{}", err))
    72    }
    73
    74    #[test]
    75    fn expand_default() {
    76        assert_data_eq!(expand(r#"fn main() {}"#), str![""]);
    77    }
    78
    79    #[test]
    80    fn expand_dependencies() {
    81        assert_data_eq!(
    82            expand(
    83                r#"---cargo
    84[dependencies]
    85time="0.1.25"
    86---
    87fn main() {}
    88"#
    89            ),
    90            str![[r##"
    91#---cargo
    92[dependencies]
    93time="0.1.25"
    94
    95"##]]
    96        );
    97    }
    98}