1use std::{
4 collections::{BTreeMap, HashSet},
5 sync::{Arc, OnceLock},
6};
7
8use crate::{Error, backend::Backend, query::Query};
9
10#[derive(Clone, Copy, Default)]
12pub struct Queries<'a> {
13 pub highlights: &'a str,
15 pub injections: &'a str,
17 pub locals: &'a str,
19 pub rainbows: &'a str,
21}
22
23pub(crate) struct Language<B: Backend> {
24 raw: B::Language,
25 compiled: OnceLock<Compiled<B>>,
26 sources: Option<Queries<'static>>,
27}
28
29pub(crate) struct Compiled<B: Backend> {
30 pub highlights: Query<B::Query>,
31 pub injections: Query<B::Query>,
32 pub locals: Query<B::Query>,
33 pub rainbows: Query<B::Query>,
34}
35
36pub(crate) struct Registry<B: Backend> {
37 languages: BTreeMap<String, Arc<Language<B>>>,
38 aliases: BTreeMap<String, String>,
39}
40
41impl<B: Backend> Clone for Registry<B> {
42 fn clone(&self) -> Self {
43 Self {
44 languages: self.languages.clone(),
45 aliases: self.aliases.clone(),
46 }
47 }
48}
49
50impl<B: Backend> Registry<B> {
51 pub fn new() -> Self {
52 Self {
53 languages: BTreeMap::new(),
54 aliases: BTreeMap::new(),
55 }
56 }
57
58 pub fn load(
59 &mut self,
60 backend: &mut B,
61 id: &str,
62 aliases: &[&str],
63 language: B::Language,
64 queries: Queries<'_>,
65 ) -> Result<(), Error> {
66 self.validate(id, aliases)?;
67 let compiled = Self::compile(backend, &language, queries)?;
68 self.insert(
69 id,
70 aliases,
71 Language {
72 raw: language,
73 compiled: OnceLock::from(compiled),
74 sources: None,
75 },
76 );
77 Ok(())
78 }
79
80 pub fn load_lazy(
81 &mut self,
82 id: &str,
83 aliases: &[&str],
84 language: B::Language,
85 queries: Queries<'static>,
86 ) -> Result<(), Error> {
87 self.validate(id, aliases)?;
88 self.insert(
89 id,
90 aliases,
91 Language {
92 raw: language,
93 compiled: OnceLock::new(),
94 sources: Some(queries),
95 },
96 );
97 Ok(())
98 }
99
100 fn validate(&self, id: &str, aliases: &[&str]) -> Result<(), Error> {
101 let mut names = HashSet::new();
102 for name in std::iter::once(id).chain(aliases.iter().copied()) {
103 if name.is_empty()
104 || !names.insert(name)
105 || self.languages.contains_key(name)
106 || self.aliases.contains_key(name)
107 {
108 return Err(Error::DuplicateLanguage(name.to_owned()));
109 }
110 }
111
112 Ok(())
113 }
114
115 fn compile(
116 backend: &mut B,
117 language: &B::Language,
118 queries: Queries<'_>,
119 ) -> Result<Compiled<B>, Error> {
120 Ok(Compiled {
121 highlights: backend.compile(language, queries.highlights)?,
122 injections: backend.compile(language, queries.injections)?,
123 locals: backend.compile(language, queries.locals)?,
124 rainbows: backend.compile(language, queries.rainbows)?,
125 })
126 }
127
128 fn insert(&mut self, id: &str, aliases: &[&str], language: Language<B>) {
129 self.languages.insert(id.to_owned(), Arc::new(language));
130 for alias in aliases {
131 self.aliases.insert((*alias).to_owned(), id.to_owned());
132 }
133 }
134
135 pub fn get(&self, name: &str) -> Option<(&str, &Language<B>)> {
136 let id = self.aliases.get(name).map_or(name, String::as_str);
137 self.languages
138 .get_key_value(id)
139 .map(|(id, language)| (id.as_str(), language.as_ref()))
140 }
141
142 pub fn resolve(&self, name: &str) -> Result<&str, Error> {
143 self.get(name)
144 .map(|(id, _)| id)
145 .ok_or_else(|| Error::UnknownLanguage(name.to_owned()))
146 }
147
148 pub fn language(
149 &self,
150 backend: &mut B,
151 id: &str,
152 ) -> Result<(&B::Language, &Compiled<B>), Error> {
153 let language = &self.languages[id];
154 if language.compiled.get().is_none() {
155 let compiled = Self::compile(
156 backend,
157 &language.raw,
158 language.sources.expect("lazy query sources"),
159 )?;
160 let _ = language.compiled.set(compiled);
162 }
163 Ok((
164 &language.raw,
165 language.compiled.get().expect("compiled above"),
166 ))
167 }
168}