aldrin_parser/resolver/
fs.rs1use super::{Resolver, SchemaFile};
2use std::borrow::Cow;
3use std::collections::hash_map::{Entry, HashMap};
4use std::fs;
5use std::io::Error;
6use std::path::{Path, PathBuf};
7
8#[derive(Debug)]
9pub struct FilesystemResolver {
10 include_paths: Vec<PathBuf>,
11 main_schema: Schema,
12 schemas: HashMap<String, Schema>,
13}
14
15impl FilesystemResolver {
16 pub fn new(main_schema: impl AsRef<Path>) -> Self {
17 Self {
18 include_paths: Vec::new(),
19 main_schema: Schema::open(main_schema.as_ref()),
20 schemas: HashMap::new(),
21 }
22 }
23
24 pub fn with_include_paths<T>(main_schema: impl AsRef<Path>, include_paths: T) -> Self
25 where
26 T: IntoIterator,
27 T::Item: Into<PathBuf>,
28 {
29 let mut this = Self::new(main_schema);
30 this.add_include_paths(include_paths);
31 this
32 }
33
34 pub fn add_include_path(&mut self, include_path: impl Into<PathBuf>) -> &mut Self {
35 self.include_paths.push(include_path.into());
36 self
37 }
38
39 pub fn add_include_paths<T>(&mut self, include_paths: T) -> &mut Self
40 where
41 T: IntoIterator,
42 T::Item: Into<PathBuf>,
43 {
44 self.include_paths
45 .extend(include_paths.into_iter().map(Into::into));
46 self
47 }
48}
49
50impl Resolver for FilesystemResolver {
51 fn main_schema(&self) -> SchemaFile<'_> {
52 self.main_schema.as_schema_file()
53 }
54
55 fn resolve(&mut self, name: &str) -> Option<SchemaFile<'_>> {
56 if name == self.main_schema.name {
57 return Some(self.main_schema.as_schema_file());
58 }
59
60 let entry = match self.schemas.entry(name.to_owned()) {
61 Entry::Occupied(entry) => return Some(entry.into_mut().as_schema_file()),
62 Entry::Vacant(entry) => entry,
63 };
64
65 let path = self
66 .include_paths
67 .iter()
68 .rev()
69 .cloned()
70 .find_map(|mut path| {
71 path.push(name);
72 path.set_extension("aldrin");
73
74 if path.is_file() {
75 Some(path)
76 } else {
77 None
78 }
79 })?;
80
81 let schema = Schema::open_with_name(name.to_owned(), &path);
82 let schema = entry.insert(schema);
83 Some(schema.as_schema_file())
84 }
85}
86
87#[derive(Debug)]
88struct Schema {
89 name: String,
90 path: String,
91 source: Result<String, Error>,
92}
93
94impl Schema {
95 fn open(path: &Path) -> Self {
96 let name = Self::schema_name_from_path(path).into_owned();
97 Self::open_with_name(name, path)
98 }
99
100 fn open_with_name(name: String, path: &Path) -> Self {
101 Self {
102 name,
103 path: path.to_string_lossy().into_owned(),
104 source: fs::read_to_string(path),
105 }
106 }
107
108 fn schema_name_from_path(path: &Path) -> Cow<'_, str> {
109 match path.file_stem() {
110 Some(stem) => stem.to_string_lossy(),
111 None => Cow::Borrowed(""),
112 }
113 }
114
115 fn as_schema_file(&self) -> SchemaFile<'_> {
116 SchemaFile::new(&self.name, &self.path, self.source.as_deref())
117 }
118}