1use anyhow::{Context, Result};
4use indexmap::IndexMap;
5use serde_derive::Deserialize;
6use std::{
7 fs,
8 path::{Path, PathBuf},
9 str::FromStr,
10};
11
12#[derive(Debug, Clone, Deserialize, Default)]
14#[serde(rename_all = "kebab-case", deny_unknown_fields)]
15pub struct Dependency {
16 pub path: PathBuf,
18}
19
20impl FromStr for Dependency {
21 type Err = ();
22
23 fn from_str(s: &str) -> Result<Self, Self::Err> {
24 Ok(Self { path: s.into() })
25 }
26}
27
28#[derive(Debug, Clone, Deserialize, Default)]
30#[serde(rename_all = "kebab-case", deny_unknown_fields)]
31pub struct InstantiationArg {
32 pub instance: String,
34
35 #[serde(default)]
39 pub export: Option<String>,
40}
41
42impl FromStr for InstantiationArg {
43 type Err = ();
44
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 Ok(Self {
47 instance: s.to_string(),
48 export: None,
49 })
50 }
51}
52
53#[derive(Debug, Clone, Deserialize, Default)]
55#[serde(rename_all = "kebab-case", deny_unknown_fields)]
56pub struct Instantiation {
57 pub dependency: Option<String>,
61
62 #[serde(default, deserialize_with = "de::index_map")]
67 pub arguments: IndexMap<String, InstantiationArg>,
68}
69
70#[derive(Default, Debug, Clone, Deserialize)]
72#[serde(rename_all = "kebab-case", deny_unknown_fields)]
73pub struct Config {
74 #[serde(skip)]
78 pub dir: PathBuf,
79
80 #[serde(default)]
82 pub definitions: Vec<PathBuf>,
83
84 #[serde(default)]
88 pub search_paths: Vec<PathBuf>,
89
90 #[serde(default)]
92 pub skip_validation: bool,
93
94 #[serde(default)]
99 pub import_components: bool,
100
101 #[serde(default)]
106 pub disallow_imports: bool,
107
108 #[serde(default, deserialize_with = "de::index_map")]
110 pub dependencies: IndexMap<String, Dependency>,
111
112 #[serde(default)]
114 pub instantiations: IndexMap<String, Instantiation>,
115}
116
117impl Config {
118 pub fn from_file(path: impl Into<PathBuf>) -> Result<Self> {
120 let path = path.into();
121
122 log::info!("reading configuration file `{}`", path.display());
123
124 let config = fs::read_to_string(&path)
125 .with_context(|| format!("failed to read configuration file `{}`", path.display()))?;
126
127 let mut config: Config = serde_yaml::from_str(&config)
128 .with_context(|| format!("failed to parse configuration file `{}`", path.display()))?;
129
130 config.dir = path.parent().map(Path::to_path_buf).unwrap_or_default();
131
132 Ok(config)
133 }
134
135 pub fn dependency_name<'a>(&'a self, instance: &'a str) -> &'a str {
137 self.instantiations
138 .get(instance)
139 .and_then(|i| i.dependency.as_deref())
140 .unwrap_or(instance)
141 }
142}
143
144mod de {
145 use indexmap::IndexMap;
146 use serde::{
147 Deserialize, Deserializer,
148 de::{self, MapAccess, Visitor},
149 };
150 use std::{fmt, hash::Hash, marker::PhantomData, str::FromStr};
151
152 pub fn index_map<'de, K, V, D>(deserializer: D) -> Result<IndexMap<K, V>, D::Error>
155 where
156 K: Hash + Eq + Deserialize<'de>,
157 V: Deserialize<'de> + FromStr<Err = ()>,
158 D: Deserializer<'de>,
159 {
160 deserializer.deserialize_map(MapVisitor(PhantomData))
161 }
162
163 struct MapVisitor<K, V>(PhantomData<(K, V)>);
164
165 impl<'de, K, V> Visitor<'de> for MapVisitor<K, V>
166 where
167 K: Hash + Eq + Deserialize<'de>,
168 V: Deserialize<'de> + FromStr<Err = ()>,
169 {
170 type Value = IndexMap<K, V>;
171
172 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
173 formatter.write_str("map")
174 }
175
176 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
177 where
178 M: MapAccess<'de>,
179 {
180 struct Wrapper<V>(V);
181
182 impl<'de, V> Deserialize<'de> for Wrapper<V>
183 where
184 V: Deserialize<'de> + FromStr<Err = ()>,
185 {
186 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
187 where
188 D: Deserializer<'de>,
189 {
190 Ok(Self(
191 deserializer.deserialize_any(StringOrMapVisitor(PhantomData))?,
192 ))
193 }
194 }
195
196 let mut map = Self::Value::with_capacity(access.size_hint().unwrap_or(0));
197 while let Some((key, value)) = access.next_entry::<_, Wrapper<V>>()? {
198 map.insert(key, value.0);
199 }
200
201 Ok(map)
202 }
203 }
204
205 struct StringOrMapVisitor<V>(PhantomData<V>);
206
207 impl<'de, V> Visitor<'de> for StringOrMapVisitor<V>
208 where
209 V: Deserialize<'de> + FromStr<Err = ()>,
210 {
211 type Value = V;
212
213 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
214 formatter.write_str("string or map")
215 }
216
217 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
218 where
219 E: de::Error,
220 {
221 Ok(V::from_str(value).unwrap())
222 }
223
224 fn visit_map<M>(self, access: M) -> Result<Self::Value, M::Error>
225 where
226 M: MapAccess<'de>,
227 {
228 Deserialize::deserialize(de::value::MapAccessDeserializer::new(access))
229 }
230 }
231}