1use std::{collections::HashMap, ffi::OsString, path::Path};
2
3use crate::resolver::{HeuristicRule, Language};
4
5pub trait Container {
10 fn get_language_by_name(&self, name: &str) -> Option<&Language>;
12 fn get_languages_by_extension(&self, file: impl AsRef<Path>) -> Option<Vec<&Language>>;
14 fn get_languages_by_filename(&self, file: impl AsRef<Path>) -> Option<Vec<&Language>>;
16 fn get_languages_by_interpreter(&self, interpreter: &str) -> Option<Vec<&Language>>;
18 #[cfg(feature = "matcher")]
20 fn get_heuristics_by_extension(&self, file: impl AsRef<Path>) -> Option<&Vec<HeuristicRule>>;
21}
22
23#[derive(Debug, Default)]
24pub struct InMemoryLanguageContainer {
25 languages: Vec<Language>,
26 heuristics: HashMap<OsString, Vec<HeuristicRule>>,
27}
28
29impl InMemoryLanguageContainer {
30 pub fn register_language(&mut self, lang: impl Into<Language>) {
31 self.languages.push(lang.into());
32 }
33
34 #[cfg(feature = "matcher")]
35 pub fn register_heuristic_rule(&mut self, rule: impl Into<HeuristicRule>) {
36 let rule = rule.into();
37
38 for ext in &rule.extensions {
39 if let Some(heuristic) = self.heuristics.get_mut(ext) {
40 if !heuristic.contains(&rule) {
41 heuristic.push(rule.clone());
42 } else {
43 }
44 } else {
45 self.heuristics
46 .insert(ext.to_os_string(), vec![rule.clone()]);
47 }
48 }
49 }
50}
51
52impl Container for InMemoryLanguageContainer {
53 fn get_language_by_name(&self, name: &str) -> Option<&Language> {
54 self.languages
55 .iter()
56 .find(|lang| lang.name.to_lowercase() == *name.to_lowercase())
57 }
58
59 fn get_languages_by_extension(&self, file: impl AsRef<Path>) -> Option<Vec<&Language>> {
60 let ext = match file.as_ref().extension() {
61 Some(ext) => ext,
62 _ => match file.as_ref().file_name() {
63 Some(name) => name,
64 _ => return None,
65 },
66 };
67
68 let candidates: Vec<&Language> = self
69 .languages
70 .iter()
71 .filter(|lang| lang.extensions.contains(&OsString::from(ext)))
72 .collect();
73
74 if !candidates.is_empty() {
75 Some(candidates)
76 } else {
77 None
78 }
79 }
80
81 fn get_languages_by_filename(&self, file: impl AsRef<Path>) -> Option<Vec<&Language>> {
82 let candidates: Vec<&Language> = self
83 .languages
84 .iter()
85 .filter(|lang| {
86 lang.filenames
87 .contains(&file.as_ref().as_os_str().to_os_string())
88 })
89 .collect();
90
91 if !candidates.is_empty() {
92 Some(candidates)
93 } else {
94 None
95 }
96 }
97
98 #[cfg(feature = "matcher")]
99 fn get_heuristics_by_extension(&self, file: impl AsRef<Path>) -> Option<&Vec<HeuristicRule>> {
100 let ext = match file.as_ref().extension() {
101 Some(val) => val,
102 _ => return None,
103 };
104
105 let heuristics = self.heuristics.get(&ext.to_os_string());
106 heuristics
107 }
108
109 fn get_languages_by_interpreter(&self, interpreter: &str) -> Option<Vec<&Language>> {
110 let interpreters: Vec<&Language> = self
111 .languages
112 .iter()
113 .filter(|lang| lang.interpreters.contains(&interpreter.to_string()))
114 .collect();
115
116 if !interpreters.is_empty() {
117 Some(interpreters)
118 } else {
119 None
120 }
121 }
122}