1use std::collections::BTreeSet;
23use std::sync::Arc;
24
25use serde::ser::{Serialize, SerializeMap, Serializer};
26use serde_json::Value;
27
28use crate::components::Mesh;
29use crate::json::JsonError;
30use crate::world::{Component, Entity, World};
31
32#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct Origin {
36 pub archive: Option<Arc<str>>,
37 pub file: Arc<str>,
38 pub item: Option<Arc<str>>,
39}
40
41impl Origin {
42 pub fn new(archive: Option<&str>, file: &str, item: Option<&str>) -> Self {
43 Origin {
44 archive: archive.map(Arc::from),
45 file: Arc::from(file),
46 item: item.map(Arc::from),
47 }
48 }
49
50 pub fn with_item(&self, item: &str) -> Self {
52 Origin {
53 item: Some(Arc::from(item)),
54 ..self.clone()
55 }
56 }
57}
58
59impl Serialize for Origin {
60 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
61 let n = 1 + usize::from(self.archive.is_some()) + usize::from(self.item.is_some());
62 let mut m = s.serialize_map(Some(n))?;
63 if let Some(a) = &self.archive {
64 m.serialize_entry("archive", &**a)?;
65 }
66 m.serialize_entry("file", &*self.file)?;
67 if let Some(i) = &self.item {
68 m.serialize_entry("item", &**i)?;
69 }
70 m.end()
71 }
72}
73
74#[derive(Clone, Debug, PartialEq, Eq, Default)]
76pub struct Provenance {
77 pub origins: Vec<Origin>,
79 pub parts: Vec<u32>,
82 pub passes: Vec<Arc<str>>,
84}
85
86impl Serialize for Provenance {
87 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
88 let n = 1 + usize::from(!self.parts.is_empty()) + usize::from(!self.passes.is_empty());
89 let mut m = s.serialize_map(Some(n))?;
90 m.serialize_entry("origins", &self.origins)?;
91 if !self.parts.is_empty() {
92 m.serialize_entry("parts", &self.parts)?;
93 }
94 if !self.passes.is_empty() {
95 let passes: Vec<&str> = self.passes.iter().map(|p| &**p).collect();
96 m.serialize_entry("passes", &passes)?;
97 }
98 m.end()
99 }
100}
101
102impl Component for Provenance {
103 const NAME: &'static str = "core.provenance";
104 const GEOMETRY: crate::Geometry = crate::Geometry::PerPrimitive;
105 fn to_json(&self) -> Result<Option<Value>, JsonError> {
106 crate::json::to_value(self).map(Some)
107 }
108 fn remap_primitives(&self, from: &[usize]) -> Option<Result<Self, String>> {
111 if self.parts.is_empty() {
112 return Some(Ok(self.clone()));
113 }
114 Some(crate::remap_list(&self.parts, from).map(|parts| Provenance {
115 parts,
116 ..self.clone()
117 }))
118 }
119 fn verify(&self, e: Entity, w: &World) -> Result<(), String> {
120 if self.origins.is_empty() {
121 return Err("no origins".into());
122 }
123 let mut seen = BTreeSet::new();
124 for (i, o) in self.origins.iter().enumerate() {
125 if !seen.insert(o) {
126 return Err(format!("origin {i} repeats an earlier one"));
127 }
128 }
129 if let Some(p) = self.parts.iter().find(|&&p| p as usize >= self.origins.len()) {
130 return Err(format!("part origin {p} of {} origins", self.origins.len()));
131 }
132 if !self.parts.is_empty()
133 && let Some(m) = w.get::<Mesh>(e)
134 && m.primitives().len() != self.parts.len()
135 {
136 return Err(format!(
137 "{} parts for {} primitives",
138 self.parts.len(),
139 m.primitives().len()
140 ));
141 }
142 Ok(())
143 }
144}
145
146impl Provenance {
147 pub fn new(origin: Origin) -> Self {
149 Provenance {
150 origins: vec![origin],
151 ..Provenance::default()
152 }
153 }
154
155 pub fn intern(&mut self, origin: Origin) -> u32 {
158 match self.origins.iter().position(|o| *o == origin) {
159 Some(i) => i as u32,
160 None => {
161 self.origins.push(origin);
162 (self.origins.len() - 1) as u32
163 }
164 }
165 }
166
167 pub fn push_part(&mut self, origin: Origin) {
169 let i = self.intern(origin);
170 self.parts.push(i);
171 }
172
173 pub fn merge(&mut self, other: &Provenance) {
175 let mut seen: BTreeSet<Origin> = self.origins.iter().cloned().collect();
176 for o in &other.origins {
177 if seen.insert(o.clone()) {
178 self.origins.push(o.clone());
179 }
180 }
181 for p in &other.passes {
182 if !self.passes.contains(p) {
183 self.passes.push(p.clone());
184 }
185 }
186 }
187
188 pub fn derived_by(&mut self, pass: &str) {
190 if self.passes.last().map(|p| &**p) != Some(pass) {
191 self.passes.push(Arc::from(pass));
192 }
193 }
194}
195
196pub fn derive(w: &World, from: &[Entity], pass: &str) -> Option<Provenance> {
199 let mut out: Option<Provenance> = None;
200 for &e in from {
201 if let Some(p) = w.get::<Provenance>(e) {
202 match &mut out {
203 Some(o) => o.merge(p),
204 None => {
205 out = Some(Provenance {
206 parts: Vec::new(),
207 ..p.clone()
208 })
209 }
210 }
211 }
212 }
213 let mut out = out?;
214 out.derived_by(pass);
215 Some(out)
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use crate::components::{Primitive, Semantic, Stream, Topology};
222 use std::collections::BTreeMap;
223
224 fn prim() -> Primitive {
225 Primitive {
226 topology: Topology::Triangles,
227 streams: BTreeMap::from([(Semantic::Position, Stream::F32x3(vec![[0.; 3]; 3]))]),
228 indices: None,
229 material: None,
230 }
231 }
232
233 fn flver(mesh: u32) -> Origin {
234 Origin::new(
235 Some("map/m11/m11_000123.mapbnd"),
236 "m11_000123.flver",
237 Some(&format!("mesh {mesh}")),
238 )
239 }
240
241 #[test]
242 fn parts_follow_the_mesh_and_the_verifier_holds_them_to_it() {
243 let mut w = World::new();
244 let e = w.spawn();
245 w.insert(e, Mesh::new(vec![prim(), prim(), prim()]));
246 let mut p = Provenance::default();
247 for m in [0, 1, 1] {
248 p.push_part(flver(m));
249 }
250 assert_eq!((p.origins.len(), &p.parts[..]), (2, &[0, 1, 1][..]));
251 w.insert(e, p.clone());
252 assert_eq!(w.verify_components(), []);
253
254 *w.get_mut::<Mesh>(e).unwrap() = Mesh::new(vec![prim(), prim(), prim(), prim()]);
256 let failures = w.verify_components();
257 assert_eq!(failures.len(), 1);
258 assert_eq!(failures[0].2, "3 parts for 4 primitives");
259 assert_eq!(
261 w.remap_primitives(e, &[4]).unwrap_err(),
262 "core.provenance: primitive 4 of 3"
263 );
264 w.remap_primitives(e, &[1, 2, 2, 2]).unwrap();
265 assert_eq!(w.get::<Provenance>(e).unwrap().parts, [1, 1, 1, 1]);
266 assert_eq!(w.verify_components(), []);
267 let mut p = w.get::<Provenance>(e).unwrap().clone();
268 p.derived_by("split{max=2}");
269 let whole = Provenance::new(flver(0));
271 assert_eq!(whole.remap_primitives(&[0, 0]), Some(Ok(whole.clone())));
272
273 for bad in [
275 Provenance {
276 parts: vec![0, 0, 0, 2],
277 ..p.clone()
278 },
279 Provenance {
280 origins: vec![flver(0), flver(0)],
281 parts: vec![],
282 passes: vec![],
283 },
284 Provenance::default(),
285 ] {
286 w.insert(e, bad);
287 assert_eq!(w.verify_components().len(), 1);
288 }
289 }
290
291 #[test]
292 fn derive_merges_origins_and_appends_the_pass() {
293 let mut w = World::new();
294 let a = w.spawn();
295 let b = w.spawn();
296 let c = w.spawn();
297 let mut pa = Provenance::new(flver(0));
298 pa.derived_by("load-textures");
299 w.insert(a, pa);
300 w.insert(b, Provenance::new(flver(1)));
301 let d = derive(&w, &[a, b, c, a], "bake{size=512}").unwrap();
302 assert_eq!(d.origins, [flver(0), flver(1)]);
303 assert_eq!(d.parts, Vec::<u32>::new());
304 assert_eq!(
305 d.passes.iter().map(|p| &**p).collect::<Vec<_>>(),
306 ["load-textures", "bake{size=512}"]
307 );
308 assert_eq!(derive(&w, &[c], "bake"), None);
309 assert_eq!(
310 d.to_json().unwrap().unwrap().to_string(),
311 r#"{"origins":[{"archive":"map/m11/m11_000123.mapbnd","file":"m11_000123.flver","item":"mesh 0"},{"archive":"map/m11/m11_000123.mapbnd","file":"m11_000123.flver","item":"mesh 1"}],"passes":["load-textures","bake{size=512}"]}"#
312 );
313 let bare = Provenance::new(Origin::new(None, "a.ytd", None));
314 assert_eq!(
315 bare.to_json().unwrap().unwrap().to_string(),
316 r#"{"origins":[{"file":"a.ytd"}]}"#
317 );
318 }
319}