swiftide_integrations/treesitter/
supported_languages.rs

1//! This module defines the supported programming languages for the Swiftide project and provides
2//! utility functions for mapping these languages to their respective file extensions and
3//! tree-sitter language objects.
4//!
5//! The primary purpose of this module is to facilitate the recognition and handling of different
6//! programming languages by mapping file extensions and converting language enums to tree-sitter
7//! language objects for accurate parsing and syntax analysis.
8//!
9//! # Supported Languages
10//! - Rust
11//! - Typescript
12//! - Python
13//! - Ruby
14//! - Javascript
15//! - Solidity
16
17use std::hash::Hash;
18#[allow(unused_imports)]
19pub use std::str::FromStr as _;
20
21use serde::{Deserialize, Serialize};
22
23/// Enum representing the supported programming languages in the Swiftide project.
24///
25/// This enum is used to map programming languages to their respective file extensions and
26/// tree-sitter language objects. The `EnumString` and `Display` macros from the `strum_macros`
27/// crate are used to provide string conversion capabilities. The `ascii_case_insensitive` attribute
28/// allows for case-insensitive string matching.
29#[derive(
30    Debug,
31    PartialEq,
32    Eq,
33    Clone,
34    Copy,
35    Deserialize,
36    Serialize,
37    strum_macros::EnumString,
38    strum_macros::Display,
39    strum_macros::EnumIter,
40    strum_macros::AsRefStr,
41)]
42#[strum(ascii_case_insensitive)]
43#[non_exhaustive]
44pub enum SupportedLanguages {
45    #[serde(alias = "rust")]
46    Rust,
47    #[serde(alias = "typescript")]
48    Typescript,
49    #[serde(alias = "python")]
50    Python,
51    #[serde(alias = "ruby")]
52    Ruby,
53    #[serde(alias = "javascript")]
54    Javascript,
55    #[serde(alias = "java")]
56    Java,
57    #[serde(alias = "go")]
58    Go,
59    #[serde(rename = "c-sharp", alias = "csharp", alias = "c#", alias = "C#")]
60    #[strum(
61        serialize = "csharp",
62        serialize = "c-sharp",
63        serialize = "c#",
64        serialize = "C#",
65        to_string = "c-sharp"
66    )]
67    CSharp,
68    #[serde(alias = "solidity")]
69    Solidity,
70    #[serde(alias = "c")]
71    C,
72    #[serde(alias = "cpp", alias = "c++", alias = "C++", rename = "C++")]
73    #[strum(
74        serialize = "c++",
75        serialize = "cpp",
76        serialize = "Cpp",
77        to_string = "C++"
78    )]
79    Cpp,
80    #[serde(alias = "elixir")]
81    Elixir,
82    #[serde(alias = "html", alias = "Html")]
83    HTML,
84    #[serde(alias = "php", alias = "PHP", alias = "Php")]
85    PHP,
86}
87
88impl Hash for SupportedLanguages {
89    /// Implements the `Hash` trait for `SupportedLanguages`.
90    ///
91    /// This allows instances of `SupportedLanguages` to be used as keys in hash maps and sets.
92    ///
93    /// # Parameters
94    /// - `state`: The mutable state to which the hash is added.
95    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
96        self.as_ref().hash(state);
97    }
98}
99
100/// Static array of file extensions for Rust files.
101static RUST_EXTENSIONS: &[&str] = &["rs"];
102
103/// Static array of file extensions for Typescript files.
104static TYPESCRIPT_EXTENSIONS: &[&str] = &["ts", "tsx", "js", "jsx"];
105
106/// Static array of file extensions for Python files.
107static PYTHON_EXTENSIONS: &[&str] = &["py"];
108
109/// Static array of file extensions for Ruby files.
110static RUBY_EXTENSIONS: &[&str] = &["rb"];
111
112/// Static array of file extensions for Javascript files.
113static JAVASCRIPT_EXTENSIONS: &[&str] = &["js", "jsx"];
114
115/// Static array of file extensions for Java files.
116static JAVA_EXTENSIONS: &[&str] = &["java"];
117
118/// Static array of file extensions for Go files.
119static GO_EXTENSIONS: &[&str] = &["go"];
120
121/// Static array of file extensions for C# files.
122static C_SHARP_EXTENSIONS: &[&str] = &["cs", "csx"];
123
124/// Static array of file extensions for Solidity files.
125static SOLIDITY_EXTENSIONS: &[&str] = &["sol"];
126
127/// Static array of file extensions for C files.
128static C_EXTENSIONS: &[&str] = &["c", "h", "o"];
129
130/// Static array of file extensions for C++ files.
131static CPP_EXTENSIONS: &[&str] = &["c", "h", "o", "cc", "cpp"];
132
133static ELIXIR_EXTENSIONS: &[&str] = &["ex", "exs"];
134
135static HTML_EXTENSIONS: &[&str] = &["html", "htm", "xhtml"];
136
137static PHP_EXTENSIONS: &[&str] = &["php"];
138
139impl SupportedLanguages {
140    /// Returns the file extensions associated with the supported language.
141    ///
142    /// # Returns
143    /// A static slice of string slices representing the file extensions.
144    pub fn file_extensions(&self) -> &[&str] {
145        match self {
146            SupportedLanguages::Rust => RUST_EXTENSIONS,
147            SupportedLanguages::Typescript => TYPESCRIPT_EXTENSIONS,
148            SupportedLanguages::Python => PYTHON_EXTENSIONS,
149            SupportedLanguages::Ruby => RUBY_EXTENSIONS,
150            SupportedLanguages::Javascript => JAVASCRIPT_EXTENSIONS,
151            SupportedLanguages::Java => JAVA_EXTENSIONS,
152            SupportedLanguages::Go => GO_EXTENSIONS,
153            SupportedLanguages::CSharp => C_SHARP_EXTENSIONS,
154            SupportedLanguages::Solidity => SOLIDITY_EXTENSIONS,
155            SupportedLanguages::C => C_EXTENSIONS,
156            SupportedLanguages::Cpp => CPP_EXTENSIONS,
157            SupportedLanguages::Elixir => ELIXIR_EXTENSIONS,
158            SupportedLanguages::HTML => HTML_EXTENSIONS,
159            SupportedLanguages::PHP => PHP_EXTENSIONS,
160        }
161    }
162}
163
164impl From<SupportedLanguages> for tree_sitter::Language {
165    /// Converts a `SupportedLanguages` enum to a `tree_sitter::Language` object.
166    ///
167    /// This implementation allows for the conversion of the supported languages to their respective
168    /// tree-sitter language objects, enabling accurate parsing and syntax analysis.
169    ///
170    /// # Parameters
171    /// - `val`: The `SupportedLanguages` enum value to be converted.
172    ///
173    /// # Returns
174    /// A `tree_sitter::Language` object corresponding to the provided `SupportedLanguages` enum
175    /// value.
176    fn from(val: SupportedLanguages) -> Self {
177        match val {
178            SupportedLanguages::Rust => tree_sitter_rust::LANGUAGE,
179            SupportedLanguages::Python => tree_sitter_python::LANGUAGE,
180            SupportedLanguages::Typescript => tree_sitter_typescript::LANGUAGE_TYPESCRIPT,
181            SupportedLanguages::Javascript => tree_sitter_javascript::LANGUAGE,
182            SupportedLanguages::Ruby => tree_sitter_ruby::LANGUAGE,
183            SupportedLanguages::Java => tree_sitter_java::LANGUAGE,
184            SupportedLanguages::Go => tree_sitter_go::LANGUAGE,
185            SupportedLanguages::CSharp => tree_sitter_c_sharp::LANGUAGE,
186            SupportedLanguages::Solidity => tree_sitter_solidity::LANGUAGE,
187            SupportedLanguages::C => tree_sitter_c::LANGUAGE,
188            SupportedLanguages::Cpp => tree_sitter_cpp::LANGUAGE,
189            SupportedLanguages::Elixir => tree_sitter_elixir::LANGUAGE,
190            SupportedLanguages::HTML => tree_sitter_html::LANGUAGE,
191            SupportedLanguages::PHP => tree_sitter_php::LANGUAGE_PHP,
192        }
193        .into()
194    }
195}
196
197#[cfg(test)]
198mod test {
199    use super::*;
200    pub use strum::IntoEnumIterator as _;
201
202    /// Tests the case-insensitive string conversion for `SupportedLanguages`.
203    #[test]
204    fn test_supported_languages_from_str() {
205        assert_eq!(
206            SupportedLanguages::from_str("rust"),
207            Ok(SupportedLanguages::Rust)
208        );
209        assert_eq!(
210            SupportedLanguages::from_str("typescript"),
211            Ok(SupportedLanguages::Typescript)
212        );
213        assert_eq!(
214            SupportedLanguages::from_str("java"),
215            Ok(SupportedLanguages::Java)
216        );
217        assert_eq!(
218            SupportedLanguages::from_str("c-sharp"),
219            Ok(SupportedLanguages::CSharp)
220        );
221    }
222
223    /// Tests the case-insensitive string conversion for `SupportedLanguages` with different casing.
224    #[test]
225    fn test_supported_languages_from_str_case_insensitive() {
226        assert_eq!(
227            SupportedLanguages::from_str("Rust"),
228            Ok(SupportedLanguages::Rust)
229        );
230        assert_eq!(
231            SupportedLanguages::from_str("TypeScript"),
232            Ok(SupportedLanguages::Typescript)
233        );
234
235        assert_eq!(
236            SupportedLanguages::from_str("Java"),
237            Ok(SupportedLanguages::Java)
238        );
239        assert_eq!(
240            SupportedLanguages::from_str("C-Sharp"),
241            Ok(SupportedLanguages::CSharp)
242        );
243        assert_eq!(
244            SupportedLanguages::from_str("C++"),
245            Ok(SupportedLanguages::Cpp)
246        );
247        assert_eq!(
248            SupportedLanguages::from_str("cpp"),
249            Ok(SupportedLanguages::Cpp)
250        );
251
252        assert_eq!(
253            SupportedLanguages::from_str("elixir"),
254            Ok(SupportedLanguages::Elixir)
255        );
256    }
257
258    #[test]
259    fn test_serialize_and_deserialize_for_supported_languages() {
260        for lang in SupportedLanguages::iter() {
261            let val = serde_json::to_string(&lang).unwrap();
262
263            assert_eq!(
264                serde_json::to_string(&lang).unwrap(),
265                format!("\"{lang}\""),
266                "Failed to serialize {lang}"
267            );
268            assert_eq!(
269                serde_json::from_str::<SupportedLanguages>(&val).unwrap(),
270                lang,
271                "Failed to deserialize {lang}"
272            );
273            assert_eq!(
274                serde_json::from_str::<SupportedLanguages>(&val.to_lowercase()).unwrap(),
275                lang,
276                "Failed to deserialize lowercase {lang}"
277            );
278        }
279    }
280}