Skip to main content

codelens_core/language/
definition.rs

1//! Language definition structures.
2
3use serde::Deserialize;
4
5/// Definition of a programming language.
6#[derive(Debug, Clone, Deserialize)]
7pub struct Language {
8    /// Language display name (e.g., "Rust", "Python").
9    pub name: String,
10
11    /// File extensions (e.g., [".rs", ".rlib"]).
12    #[serde(default)]
13    pub extensions: Vec<String>,
14
15    /// Special filenames (e.g., ["Makefile", "Dockerfile"]).
16    #[serde(default)]
17    pub filenames: Vec<String>,
18
19    /// Single-line comment prefixes (e.g., ["//", "#"]).
20    #[serde(default)]
21    pub line_comments: Vec<String>,
22
23    /// Block comment delimiters (e.g., [("/*", "*/")]).
24    #[serde(default, deserialize_with = "deserialize_block_comments")]
25    pub block_comments: Vec<(String, String)>,
26
27    /// String literal delimiters for accurate parsing.
28    #[serde(default)]
29    pub string_delimiters: Vec<StringDelimiter>,
30
31    /// Regex pattern to match function definitions.
32    #[serde(default)]
33    pub function_pattern: Option<String>,
34
35    /// Keywords that contribute to cyclomatic complexity.
36    #[serde(default)]
37    pub complexity_keywords: Vec<String>,
38
39    /// Whether block comments can be nested (e.g., Rust allows /* /* */ */).
40    #[serde(default)]
41    pub nested_comments: bool,
42}
43
44impl Default for Language {
45    fn default() -> Self {
46        Self {
47            name: "Unknown".to_string(),
48            extensions: vec![],
49            filenames: vec![],
50            line_comments: vec![],
51            block_comments: vec![],
52            string_delimiters: vec![],
53            function_pattern: None,
54            complexity_keywords: vec![],
55            nested_comments: false,
56        }
57    }
58}
59
60/// String delimiter definition.
61#[derive(Debug, Clone, Deserialize)]
62pub struct StringDelimiter {
63    /// Opening delimiter.
64    pub start: String,
65    /// Closing delimiter.
66    pub end: String,
67    /// Escape character (if any).
68    #[serde(default)]
69    pub escape: Option<String>,
70}
71
72/// Custom deserializer for block comments that handles TOML array format.
73fn deserialize_block_comments<'de, D>(deserializer: D) -> Result<Vec<(String, String)>, D::Error>
74where
75    D: serde::Deserializer<'de>,
76{
77    let raw: Vec<Vec<String>> = Vec::deserialize(deserializer)?;
78    Ok(raw
79        .into_iter()
80        .filter_map(|pair| {
81            if pair.len() >= 2 {
82                Some((pair[0].clone(), pair[1].clone()))
83            } else {
84                None
85            }
86        })
87        .collect())
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_language_default() {
96        let lang = Language::default();
97        assert_eq!(lang.name, "Unknown");
98        assert!(lang.extensions.is_empty());
99        assert!(lang.line_comments.is_empty());
100    }
101
102    #[test]
103    fn test_language_deserialize() {
104        let toml = r#"
105            name = "Rust"
106            extensions = [".rs"]
107            line_comments = ["//"]
108            block_comments = [["/*", "*/"]]
109            function_pattern = "fn\\s+\\w+"
110            complexity_keywords = ["if", "for", "while"]
111            nested_comments = true
112        "#;
113
114        let lang: Language = toml::from_str(toml).unwrap();
115        assert_eq!(lang.name, "Rust");
116        assert_eq!(lang.extensions, vec![".rs"]);
117        assert_eq!(lang.line_comments, vec!["//"]);
118        assert_eq!(
119            lang.block_comments,
120            vec![("/*".to_string(), "*/".to_string())]
121        );
122        assert!(lang.nested_comments);
123    }
124}