libgir/analysis/
imports.rs1use std::{
2 borrow::Cow,
3 cmp::Ordering,
4 collections::{btree_map::BTreeMap, HashSet},
5 ops::{Deref, DerefMut},
6 vec::IntoIter,
7};
8
9use super::namespaces;
10use crate::{library::Library, nameutil::crate_name, version::Version};
11
12fn is_first_char_up(s: &str) -> bool {
13 s.chars().next().unwrap().is_uppercase()
14}
15
16fn check_up_eq(a: &str, b: &str) -> Ordering {
17 let is_a_up = is_first_char_up(a);
18 let is_b_up = is_first_char_up(b);
19 if is_a_up != is_b_up {
20 if is_a_up {
21 return Ordering::Greater;
22 }
23 return Ordering::Less;
24 }
25 Ordering::Equal
26}
27
28fn compare_imports(a: &(&String, &ImportConditions), b: &(&String, &ImportConditions)) -> Ordering {
41 let s = check_up_eq(a.0, b.0);
42 if s != Ordering::Equal {
43 return s;
44 }
45 let mut a = a.0.split("::");
46 let mut b = b.0.split("::");
47 loop {
48 match (a.next(), b.next()) {
49 (Some(a), Some(b)) => {
50 let s = check_up_eq(a, b);
51 if s != Ordering::Equal {
52 break s;
53 }
54 let s = a.partial_cmp(b).unwrap();
55 if s != Ordering::Equal {
56 break s;
57 }
58 }
59 (Some(_), None) => break Ordering::Greater,
60 (None, Some(_)) => break Ordering::Less,
61 (None, None) => break Ordering::Equal,
62 }
63 }
64}
65
66#[derive(Clone, Debug, Default)]
73pub struct Imports {
74 crate_name: String,
76 defined: HashSet<String>,
78 defaults: ImportConditions,
79 map: BTreeMap<String, ImportConditions>,
80}
81
82impl Imports {
83 pub fn new(gir: &Library) -> Self {
84 Self {
85 crate_name: make_crate_name(gir),
86 defined: HashSet::new(),
87 defaults: ImportConditions::default(),
88 map: BTreeMap::new(),
89 }
90 }
91
92 pub fn with_defined(gir: &Library, name: &str) -> Self {
93 Self {
94 crate_name: make_crate_name(gir),
95 defined: std::iter::once(name.to_owned()).collect(),
96 defaults: ImportConditions::default(),
97 map: BTreeMap::new(),
98 }
99 }
100
101 #[must_use = "ImportsWithDefault must live while defaults are needed"]
102 pub fn with_defaults(
103 &mut self,
104 version: Option<Version>,
105 constraint: &Option<String>,
106 ) -> ImportsWithDefault<'_> {
107 let constraints = if let Some(constraint) = constraint {
108 vec![constraint.clone()]
109 } else {
110 vec![]
111 };
112 self.defaults = ImportConditions {
113 version,
114 constraints,
115 };
116
117 ImportsWithDefault::new(self)
118 }
119
120 fn reset_defaults(&mut self) {
121 self.defaults.clear();
122 }
123
124 fn common_checks(&self, name: &str) -> bool {
128 if (!name.contains("::") && name != "xlib") || self.defined.contains(name) {
129 false
130 } else if let Some(name) = name.strip_prefix("crate::") {
131 !self.defined.contains(name)
132 } else {
133 true
134 }
135 }
136
137 pub fn add_defined(&mut self, name: &str) {
142 if self.defined.insert(name.to_owned()) {
143 self.map.remove(name);
144 }
145 }
146
147 pub fn add(&mut self, name: &str) {
152 if !self.common_checks(name) {
153 return;
154 }
155 if let Some(mut name) = self.strip_crate_name(name) {
156 if name == "xlib" {
157 name = if self.crate_name == "gdk_x11" {
158 Cow::Borrowed("x11::xlib")
160 } else {
161 Cow::Borrowed("crate::xlib")
163 };
164 }
165 let defaults = &self.defaults;
166 let entry = self
167 .map
168 .entry(name.into_owned())
169 .or_insert_with(|| defaults.clone());
170 entry.update_version(self.defaults.version);
171 entry.update_constraints(&self.defaults.constraints);
172 }
173 }
174
175 pub fn add_with_version(&mut self, name: &str, version: Option<Version>) {
179 if !self.common_checks(name) {
180 return;
181 }
182 if let Some(name) = self.strip_crate_name(name) {
183 let entry = self
184 .map
185 .entry(name.into_owned())
186 .or_insert(ImportConditions {
187 version,
188 constraints: Vec::new(),
189 });
190 entry.update_version(version);
191 entry.constraints.clear();
194 }
195 }
196
197 pub fn add_with_constraint(
202 &mut self,
203 name: &str,
204 version: Option<Version>,
205 constraint: Option<&str>,
206 ) {
207 if !self.common_checks(name) {
208 return;
209 }
210 if let Some(name) = self.strip_crate_name(name) {
211 let entry = if let Some(constraint) = constraint {
212 let constraint = String::from(constraint);
213 let entry = self
214 .map
215 .entry(name.into_owned())
216 .or_insert(ImportConditions {
217 version,
218 constraints: vec![constraint.clone()],
219 });
220 entry.add_constraint(constraint);
221 entry
222 } else {
223 let entry = self
224 .map
225 .entry(name.into_owned())
226 .or_insert(ImportConditions {
227 version,
228 constraints: Vec::new(),
229 });
230 entry.constraints.clear();
233 entry
234 };
235 entry.update_version(version);
236 }
237 }
238
239 pub fn add_used_type(&mut self, used_type: &str) {
243 if let Some(i) = used_type.find("::") {
244 if i == 0 {
245 self.add(&used_type[2..]);
246 } else {
247 self.add(&used_type[..i]);
248 }
249 } else {
250 self.add(&format!("crate::{used_type}"));
251 }
252 }
253
254 pub fn add_used_types(&mut self, used_types: &[String]) {
255 for s in used_types {
256 self.add_used_type(s);
257 }
258 }
259
260 pub fn add_used_type_with_version(&mut self, used_type: &str, version: Option<Version>) {
264 if let Some(i) = used_type.find("::") {
265 if i == 0 {
266 self.add_with_version(&used_type[2..], version);
267 } else {
268 self.add_with_version(&used_type[..i], version);
269 }
270 } else {
271 self.add_with_version(&format!("crate::{used_type}"), version);
272 }
273 }
274
275 fn strip_crate_name<'a>(&self, name: &'a str) -> Option<Cow<'a, str>> {
280 let prefix = &self.crate_name;
281 if !name.starts_with(prefix) {
282 return Some(Cow::Borrowed(name));
283 }
284 let rest = &name[prefix.len()..];
285 if rest.is_empty() {
286 None
287 } else if rest.starts_with("::") {
288 Some(Cow::Owned(format!("crate{rest}")))
289 } else {
290 Some(Cow::Borrowed(name))
292 }
293 }
294
295 pub fn iter(&self) -> IntoIter<(&String, &ImportConditions)> {
296 let mut imports = self.map.iter().collect::<Vec<_>>();
297 imports.sort_by(compare_imports);
298 imports.into_iter()
299 }
300}
301
302pub struct ImportsWithDefault<'a> {
303 imports: &'a mut Imports,
304}
305
306impl<'a> ImportsWithDefault<'a> {
307 fn new(imports: &'a mut Imports) -> Self {
308 Self { imports }
309 }
310}
311
312impl Drop for ImportsWithDefault<'_> {
313 fn drop(&mut self) {
314 self.imports.reset_defaults();
315 }
316}
317
318impl Deref for ImportsWithDefault<'_> {
319 type Target = Imports;
320 fn deref(&self) -> &Self::Target {
321 self.imports
322 }
323}
324
325impl DerefMut for ImportsWithDefault<'_> {
326 fn deref_mut(&mut self) -> &mut Self::Target {
327 self.imports
328 }
329}
330
331#[derive(Clone, Debug, Default, Ord, PartialEq, PartialOrd, Eq)]
332pub struct ImportConditions {
333 pub version: Option<Version>,
334 pub constraints: Vec<String>,
335}
336
337impl ImportConditions {
338 fn clear(&mut self) {
339 self.version = None;
340 self.constraints.clear();
341 }
342
343 fn update_version(&mut self, version: Option<Version>) {
344 if version < self.version {
345 self.version = version;
346 }
347 }
348
349 fn add_constraint(&mut self, constraint: String) {
350 if self.constraints.is_empty() {
353 return;
354 }
355 if !self.constraints.iter().any(|x| x == &constraint) {
358 self.constraints.push(constraint);
359 }
360 }
361
362 fn update_constraints(&mut self, constraints: &[String]) {
363 if self.constraints.is_empty() {
366 return;
367 }
368 if constraints.is_empty() {
369 self.constraints.clear();
372 } else {
373 for constraint in constraints {
376 if !self.constraints.iter().any(|x| x == constraint) {
377 self.constraints.push(constraint.clone());
378 }
379 }
380 }
381 }
382}
383
384fn make_crate_name(gir: &Library) -> String {
385 if gir.is_glib_crate() {
386 crate_name("GLib")
387 } else {
388 crate_name(gir.namespace(namespaces::MAIN).name.as_str())
389 }
390}