Skip to main content

cc_audit/parser/
toml.rs

1//! TOML content parser.
2
3use super::traits::{ContentParser, ContentType, ParsedContent};
4use crate::error::Result;
5
6/// Parser for TOML files (Cargo.toml, pyproject.toml, etc.).
7pub struct TomlParser;
8
9impl TomlParser {
10    /// Create a new TOML parser.
11    pub fn new() -> Self {
12        Self
13    }
14
15    /// Parse TOML content to a serde_json::Value.
16    pub fn parse_value(content: &str) -> Option<serde_json::Value> {
17        toml::from_str::<toml::Value>(content)
18            .ok()
19            .and_then(|v| serde_json::to_value(v).ok())
20    }
21
22    /// Parse TOML content to a toml::Value.
23    pub fn parse_toml_value(content: &str) -> Option<toml::Value> {
24        toml::from_str(content).ok()
25    }
26}
27
28impl Default for TomlParser {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl ContentParser for TomlParser {
35    fn parse(&self, content: &str, path: &str) -> Result<ParsedContent> {
36        let mut parsed =
37            ParsedContent::new(ContentType::Toml, content.to_string(), path.to_string());
38
39        // Try to parse as structured TOML (convert to JSON Value for uniformity)
40        if let Some(value) = Self::parse_value(content) {
41            parsed = parsed.with_structured_data(value);
42        }
43
44        Ok(parsed)
45    }
46
47    fn supported_extensions(&self) -> &[&str] {
48        &[".toml"]
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn test_parse_valid_toml() {
58        let parser = TomlParser::new();
59        let content = r#"
60[package]
61name = "test"
62version = "1.0.0"
63"#;
64        let result = parser.parse(content, "Cargo.toml").unwrap();
65
66        assert_eq!(result.content_type, ContentType::Toml);
67        assert!(result.structured_data.is_some());
68        let data = result.structured_data.unwrap();
69        assert_eq!(data["package"]["name"].as_str(), Some("test"));
70    }
71
72    #[test]
73    fn test_parse_invalid_toml() {
74        let parser = TomlParser::new();
75        let content = "invalid = toml [";
76        let result = parser.parse(content, "config.toml").unwrap();
77
78        assert_eq!(result.content_type, ContentType::Toml);
79        assert!(result.structured_data.is_none());
80    }
81
82    #[test]
83    fn test_supported_extensions() {
84        let parser = TomlParser::new();
85        assert!(parser.can_parse("Cargo.toml"));
86        assert!(parser.can_parse("pyproject.toml"));
87        assert!(!parser.can_parse("config.json"));
88    }
89
90    #[test]
91    fn test_parse_pyproject_toml() {
92        let parser = TomlParser::new();
93        let content = r#"
94[project]
95name = "myproject"
96version = "0.1.0"
97
98[project.dependencies]
99requests = "^2.28"
100"#;
101        let result = parser.parse(content, "pyproject.toml").unwrap();
102
103        assert!(result.structured_data.is_some());
104        let data = result.structured_data.unwrap();
105        assert_eq!(data["project"]["name"].as_str(), Some("myproject"));
106    }
107}