devgen_splitter/
lang.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//
// lang.rs
// Copyright (C) 2024 imotai <codego.me@gmail.com>
// Distributed under terms of the MIT license.
//

mod queries;
use queries::ALL_LANGS;

/// the language config
#[derive(Debug)]
pub struct LangConfig {
    /// e.g.: ["Typescript", "TSX"], ["Rust"]
    pub lang: &'static [&'static str],
    /// tree-sitter grammar for this language
    pub grammar: fn() -> tree_sitter::Language,
    /// file_extensions for this language
    pub file_extensions: &'static [&'static str],
    /// the query used to extract the class, function definition
    pub query: &'static str,
}

pub struct Lang;

impl Lang {
    /// Determines the language configuration based on the given filename.
    ///
    /// This method attempts to match the file extension of the provided filename
    /// with the supported language configurations. If a match is found, it returns
    /// the corresponding `LangConfig`.
    ///
    /// # Arguments
    ///
    /// * `filename` - A string slice that holds the name of the file
    ///
    /// # Returns
    ///
    /// * `Some(&'static LangConfig)` if a matching language configuration is found
    /// * `None` if no matching language configuration is found
    ///
    /// # Example
    ///
    /// ```no_run
    /// use devgen_splitter::Lang;
    /// let filename = "example.rs";
    /// if let Some(lang_config) = Lang::from_filename(filename) {
    ///     println!("Language: {:?}", lang_config.lang);
    ///     println!("File extensions: {:?}", lang_config.file_extensions);
    /// } else {
    ///     println!("Unsupported file type");
    /// }
    /// ```
    pub fn from_filename(filename: &str) -> Option<&'static LangConfig> {
        let path = std::path::Path::new(filename);
        let file_ext = path.extension().and_then(|ext| ext.to_str()).unwrap_or("");
        let lang = ALL_LANGS
            .iter()
            .find(|l| l.file_extensions.iter().any(|&ext| ext == file_ext));
        match lang {
            Some(lang) => Some(lang),
            None => None,
        }
    }
}

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

    #[rstest]
    #[case("example.rs", Some("Rust"))]
    #[case("example.ts", Some("TypeScript"))]
    #[case("unknown.xyz", None)]
    fn test_from_filename(#[case] filename: &str, #[case] expected_lang: Option<&str>) {
        let result = Lang::from_filename(filename);
        match expected_lang {
            Some(lang) => {
                assert!(result.is_some());
                assert_eq!(result.unwrap().lang[0], lang);
            }
            None => assert!(result.is_none()),
        }
    }
}