cargo-lsp 0.0.12

LSP for Cargo.toml files
//! cargo specific definitions and known values

// These are all the prefixes that keys can have (non leaf)
// It's necessary because some of these don't have any known keys under, but
// still count for table completion (ex dependencies).
const TABLES: &[&str] = &[
    "package",
    "badges",
    "features",
    "dependencies",
    "dev-dependencies",
    "build-dependencies",
    "target",
    "lints",
    "lints.clippy",
    "hints",
    "patch",
    "replace",
    "profile",
    "workspace",
];

// These are the final keys including their full path (leaves)
const PROPS: &[&str] = &[
    "package.name",
    "package.version",
    "package.authors",
    "package.edition",
    "package.rust-version",
    "package.description",
    "package.documentation",
    "package.readme",
    "package.homepage",
    "package.repository",
    "package.license",
    "package.license-file",
    "package.keywords",
    "package.categories",
    "package.workspace",
    "package.build",
    "package.links",
    "package.exclude",
    "package.include",
    "package.publish",
    "package.metadata",
    "package.default-run",
    "package.autolib",
    "package.autobins",
    "package.autoexamples",
    "package.autotests",
    "package.autobenches",
    "package.resolver",
    "lints.clippy.pedantic",
    "lints.clippy.pedantic.level",
    "lints.clippy.pedantic.priority",
];

const PROPS_DEPS: &[&str] = &[
    "version",
    "optional",
    "default-features",
    "features",
    "path",
    "git",
    "tag",
    "rev",
    "branch",
    "registry",
    "package",
];

pub fn tables(exclude: &[String]) -> Vec<String> {
    TABLES
        .iter()
        .filter(|s| !exclude.iter().any(|e| e == **s))
        .map(|s| format!("[{s}]"))
        .collect()
}

pub fn props(prefix: &str, exclude: &[String]) -> Vec<String> {
    if prefix.is_empty() {
        return PROPS.iter().map(std::string::ToString::to_string).collect();
    }
    if prefix.starts_with("dependencies.")
        || prefix.starts_with("dev-dependencies.")
        || prefix.starts_with("build-dependencies.")
        || prefix.starts_with("workspace.dependencies.")
    {
        return PROPS_DEPS
            .iter()
            .filter(|s| !exclude.iter().any(|e| *e == format!("{prefix}.{s}")))
            .map(ToString::to_string)
            .collect();
    }
    PROPS
        .iter()
        .filter(|s| !exclude.iter().any(|e| e == **s))
        .filter_map(|s| s.strip_prefix(prefix))
        .filter_map(|s| s.strip_prefix('.'))
        .map(ToString::to_string)
        .collect()
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_all() {
        let p = props("", &[]);
        assert_eq!(p, PROPS);
    }

    #[test]
    fn test_unknown() {
        let p = props("ba", &[]);
        assert!(p.is_empty());
    }

    #[test]
    fn test_package() {
        let p = props("package", &[]);
        assert_eq!(
            p,
            &[
                "name",
                "version",
                "authors",
                "edition",
                "rust-version",
                "description",
                "documentation",
                "readme",
                "homepage",
                "repository",
                "license",
                "license-file",
                "keywords",
                "categories",
                "workspace",
                "build",
                "links",
                "exclude",
                "include",
                "publish",
                "metadata",
                "default-run",
                "autolib",
                "autobins",
                "autoexamples",
                "autotests",
                "autobenches",
                "resolver",
            ]
        );
    }

    #[test]
    fn test_table_dots() {
        let p = props("lints.clippy", &[]);
        assert_eq!(p, &["pedantic", "pedantic.level", "pedantic.priority"]);
    }

    #[test]
    fn test_partial_dots() {
        let p = props("lints.clippy.pedantic", &[]);
        assert_eq!(p, &["level", "priority"]);
    }

    #[test]
    fn test_tables() {
        let t = tables(&[]);
        assert_eq!(
            t,
            TABLES.iter().map(|s| format!("[{s}]")).collect::<Vec<_>>()
        );
    }

    #[test]
    fn test_table_exclude() {
        let t = tables(&["package".to_string(), "dependencies".to_string()]);
        assert_eq!(
            t,
            &[
                "[badges]",
                "[features]",
                "[dev-dependencies]",
                "[build-dependencies]",
                "[target]",
                "[lints]",
                "[lints.clippy]",
                "[hints]",
                "[patch]",
                "[replace]",
                "[profile]",
                "[workspace]",
            ]
        );
    }

    #[test]
    fn test_package_exclude() {
        let p = props(
            "package",
            &[
                "package.name".to_string(),
                "package.version".to_string(),
                "package.edition".to_string(),
            ],
        );
        assert_eq!(
            p,
            &[
                "authors",
                "rust-version",
                "description",
                "documentation",
                "readme",
                "homepage",
                "repository",
                "license",
                "license-file",
                "keywords",
                "categories",
                "workspace",
                "build",
                "links",
                "exclude",
                "include",
                "publish",
                "metadata",
                "default-run",
                "autolib",
                "autobins",
                "autoexamples",
                "autotests",
                "autobenches",
                "resolver",
            ]
        );
    }

    #[test]
    fn test_dependencies() {
        let p = props("dependencies.x", &["dependencies.x.optional".to_string()]);
        assert_eq!(
            p,
            &[
                "version",
                "default-features",
                "features",
                "path",
                "git",
                "tag",
                "rev",
                "branch",
                "registry",
                "package",
            ]
        );
    }
}