phalus 0.7.0

Private Headless Automated License Uncoupling System — AI-powered clean room software reimplementation
Documentation
use super::ManifestError;
use crate::{Ecosystem, PackageRef, ParsedManifest};
use std::path::Path;

pub struct NpmParser;

impl NpmParser {
    pub fn detect(path: &Path) -> bool {
        path.file_name()
            .and_then(|n| n.to_str())
            .map(|n| n == "package.json")
            .unwrap_or(false)
    }

    pub fn parse(content: &str) -> Result<ParsedManifest, ManifestError> {
        let value: serde_json::Value =
            serde_json::from_str(content).map_err(|e| ManifestError::Parse(e.to_string()))?;

        let mut packages = Vec::new();

        if let Some(deps) = value.get("dependencies").and_then(|d| d.as_object()) {
            for (name, version) in deps {
                let version_constraint = version.as_str().unwrap_or("").to_string();
                packages.push(PackageRef {
                    name: name.clone(),
                    version_constraint,
                    ecosystem: Ecosystem::Npm,
                });
            }
        }

        Ok(ParsedManifest {
            manifest_type: "package.json".to_string(),
            packages,
        })
    }
}

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

    #[test]
    fn test_parse_basic_package_json() {
        let input = r#"{
            "name": "my-app",
            "version": "1.0.0",
            "dependencies": {
                "lodash": "^4.17.21",
                "express": "~4.18.2"
            }
        }"#;
        let manifest = NpmParser::parse(input).unwrap();
        assert_eq!(manifest.manifest_type, "package.json");
        assert_eq!(manifest.packages.len(), 2);

        let lodash = manifest
            .packages
            .iter()
            .find(|p| p.name == "lodash")
            .unwrap();
        assert_eq!(lodash.version_constraint, "^4.17.21");
        assert_eq!(lodash.ecosystem, Ecosystem::Npm);
    }

    #[test]
    fn test_parse_empty_dependencies() {
        let input = r#"{ "name": "empty", "version": "1.0.0" }"#;
        let manifest = NpmParser::parse(input).unwrap();
        assert_eq!(manifest.packages.len(), 0);
    }

    #[test]
    fn test_detect_package_json() {
        assert!(NpmParser::detect(Path::new("package.json")));
        assert!(NpmParser::detect(Path::new("/some/path/package.json")));
        assert!(!NpmParser::detect(Path::new("Cargo.toml")));
    }

    #[test]
    fn test_parse_invalid_json() {
        assert!(NpmParser::parse("not json").is_err());
    }
}