anodizer_core/config/
source.rs1use schemars::JsonSchema;
2use serde::{Deserialize, Deserializer, Serialize};
3
4use super::StringOrU32;
5
6#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
13#[serde(default)]
14pub struct SourceFileEntry {
15 pub src: String,
17 pub dst: Option<String>,
19 pub strip_parent: Option<bool>,
21 pub info: Option<SourceFileInfo>,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
27#[serde(default)]
28pub struct SourceFileInfo {
29 pub owner: Option<String>,
31 pub group: Option<String>,
33 pub mode: Option<StringOrU32>,
37 pub mtime: Option<String>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
42#[serde(default)]
43pub struct SourceConfig {
44 pub enabled: Option<bool>,
46 pub format: Option<String>,
48 pub name_template: Option<String>,
50 pub prefix_template: Option<String>,
53 #[serde(default, deserialize_with = "deserialize_source_files")]
55 #[schemars(schema_with = "source_files_schema")]
56 pub files: Vec<SourceFileEntry>,
57}
58
59impl SourceConfig {
60 pub fn is_enabled(&self) -> bool {
62 self.enabled.unwrap_or(false)
63 }
64
65 pub fn archive_format(&self) -> &str {
67 self.format.as_deref().unwrap_or("tar.gz")
68 }
69
70 pub fn apply_prefix_template_default(&mut self) {
83 if self.prefix_template.is_none()
84 && let Some(ref name_tpl) = self.name_template
85 {
86 self.prefix_template = Some(name_tpl.clone());
87 }
88 }
89}
90
91fn source_files_schema(
93 generator: &mut schemars::r#gen::SchemaGenerator,
94) -> schemars::schema::Schema {
95 let mut schema = generator.subschema_for::<Vec<SourceFileEntry>>();
96 if let schemars::schema::Schema::Object(ref mut obj) = schema {
97 obj.metadata().description = Some(
98 "Extra files for the source archive. Accepts strings (glob patterns), objects with src/dst/info, or a mixed array.".to_owned(),
99 );
100 }
101 schema
102}
103
104fn deserialize_source_files<'de, D>(deserializer: D) -> Result<Vec<SourceFileEntry>, D::Error>
111where
112 D: Deserializer<'de>,
113{
114 use serde::de::{self, SeqAccess, Visitor};
115
116 struct SourceFilesVisitor;
117
118 impl<'de> Visitor<'de> for SourceFilesVisitor {
119 type Value = Vec<SourceFileEntry>;
120
121 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 f.write_str("a string, a source file entry object, or an array of strings/objects")
123 }
124
125 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
126 Ok(vec![SourceFileEntry {
127 src: v.to_string(),
128 ..Default::default()
129 }])
130 }
131
132 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
133 let mut entries = Vec::new();
134 while let Some(value) = seq.next_element::<serde_yaml_ng::Value>()? {
135 match value {
136 serde_yaml_ng::Value::String(s) => {
137 entries.push(SourceFileEntry {
138 src: s,
139 ..Default::default()
140 });
141 }
142 other => {
143 let entry =
144 SourceFileEntry::deserialize(other).map_err(de::Error::custom)?;
145 entries.push(entry);
146 }
147 }
148 }
149 Ok(entries)
150 }
151
152 fn visit_map<M: de::MapAccess<'de>>(self, map: M) -> Result<Self::Value, M::Error> {
153 let entry = SourceFileEntry::deserialize(de::value::MapAccessDeserializer::new(map))?;
154 Ok(vec![entry])
155 }
156
157 fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
158 Ok(Vec::new())
159 }
160
161 fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
162 Ok(Vec::new())
163 }
164 }
165
166 deserializer.deserialize_any(SourceFilesVisitor)
167}