cargo-lsp 0.0.7

LSP for Cargo.toml files
// 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",
    "features",
    "dependencies",
    "dev-dependencies",
    "build-dependencies",
    "lints",
    "lints.clippy",
];

// These are the final keys including their full path (leaves)
const PROPS: &[&str] = &[
    "package.name",
    "package.version",
    "package.edition",
    "package.authors",
    "package.license",
    "package.description",
    "package.homepage",
    "package.repository",
    "package.documentation",
    "package.readme",
    "package.keywords",
    "package.categories",
    "lints.clippy.pedantic",
    "lints.clippy.pedantic.level",
    "lints.clippy.pedantic.priority",
];

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) -> Vec<String> {
    if prefix.is_empty() {
        return PROPS.iter().map(std::string::ToString::to_string).collect();
    }
    PROPS
        .iter()
        .filter_map(|s| s.strip_prefix(prefix))
        .filter_map(|s| s.strip_prefix('.'))
        .map(std::string::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",
                "edition",
                "authors",
                "license",
                "description",
                "homepage",
                "repository",
                "documentation",
                "readme",
                "keywords",
                "categories",
            ]
        );
    }
    
    #[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,
            &[
                "[features]",
                "[dev-dependencies]",
                "[build-dependencies]",
                "[lints]",
                "[lints.clippy]",
            ]
        );
    }
}