Download crates/nanobrush-cli/src/preflight.rs from nmarshall/nanobrush: direct link, hf CLI and curl.
- Browser
- Download file 5.78 kB
-
https://huggingface.co/nmarshall/nanobrush/resolve/main/crates/nanobrush-cli/src/preflight.rs
- Command line
-
hf download hf://nmarshall/nanobrush/crates/nanobrush-cli/src/preflight.rs
-
curl -L -o preflight.rs https://huggingface.co/nmarshall/nanobrush/resolve/main/crates/nanobrush-cli/src/preflight.rs
5.78 kB
| //! Compatibility reporting only: parsing/encoding remain authoritative validators. | |
| use mb3_dat::{Version, World}; | |
| use std::collections::HashSet; | |
| pub enum Target { | |
| Txt, | |
| Obj, | |
| Dat(Version), | |
| } | |
| impl Target { | |
| pub fn name(self) -> &'static str { | |
| match self { | |
| Self::Txt => "txt", | |
| Self::Obj => "obj", | |
| Self::Dat(Version::V201501280) => "dat-201501280", | |
| Self::Dat(Version::V201512200) => "dat-201512200", | |
| } | |
| } | |
| } | |
| pub struct Issue { | |
| pub code: &'static str, | |
| pub count: usize, | |
| pub lossy: bool, | |
| } | |
| pub struct Report { | |
| pub target: Target, | |
| pub issues: Vec<Issue>, | |
| } | |
| impl Report { | |
| pub fn blocked(&self) -> bool { | |
| self.issues.iter().any(|i| !i.lossy) | |
| } | |
| pub fn compatible(&self) -> bool { | |
| self.issues.is_empty() | |
| } | |
| pub fn json(&self) -> String { | |
| let issues = self | |
| .issues | |
| .iter() | |
| .map(|i| { | |
| format!( | |
| "{{\"code\":\"{}\",\"count\":{},\"lossy\":{}}}", | |
| i.code, i.count, i.lossy | |
| ) | |
| }) | |
| .collect::<Vec<_>>() | |
| .join(","); | |
| format!( | |
| "{{\"schema\":1,\"target\":\"{}\",\"compatible\":{},\"issues\":[{}]}}", | |
| self.target.name(), | |
| self.compatible(), | |
| issues | |
| ) | |
| } | |
| } | |
| pub fn analyze(world: &World, target: Target) -> Report { | |
| let mut issues = Vec::new(); | |
| let planes = || world.brushes.iter().flat_map(|b| &b.planes); | |
| match target { | |
| Target::Obj => { | |
| if !world.computed { | |
| issues.push(Issue { | |
| code: "computed_required", | |
| count: 1, | |
| lossy: false, | |
| }); | |
| } | |
| let n = planes().count().saturating_add(world.patches.len()); | |
| if n != 0 { | |
| issues.push(Issue { | |
| code: "editable_geometry", | |
| count: n, | |
| lossy: true, | |
| }); | |
| } | |
| let n = world | |
| .string_properties | |
| .len() | |
| .saturating_add(world.uv_properties.len()) | |
| .saturating_add( | |
| planes() | |
| .map(|p| p.strings.len().saturating_add(p.uv.len())) | |
| .fold(0usize, usize::saturating_add), | |
| ); | |
| if n != 0 { | |
| issues.push(Issue { | |
| code: "properties", | |
| count: n, | |
| lossy: true, | |
| }); | |
| } | |
| let n = world | |
| .patches | |
| .iter() | |
| .filter_map(|p| p.computed.as_ref()) | |
| .map(|p| p.normals.len()) | |
| .fold(0usize, usize::saturating_add); | |
| if n != 0 { | |
| issues.push(Issue { | |
| code: "stored_normals", | |
| count: n, | |
| lossy: true, | |
| }); | |
| } | |
| } | |
| Target::Dat(version) => { | |
| let n = world | |
| .string_properties | |
| .iter() | |
| .chain(&world.uv_properties) | |
| .filter(|s| !s.is_ascii()) | |
| .count() | |
| .saturating_add( | |
| planes() | |
| .flat_map(|p| &p.strings) | |
| .filter(|p| !p.value.is_ascii()) | |
| .count(), | |
| ); | |
| if n != 0 { | |
| issues.push(Issue { | |
| lossy: false, | |
| code: "non_ascii", | |
| count: n, | |
| }); | |
| } | |
| if version == Version::V201501280 { | |
| let n = world | |
| .string_properties | |
| .len() | |
| .saturating_add(world.uv_properties.len()) | |
| .saturating_add( | |
| planes() | |
| .map(|p| p.strings.len().saturating_add(p.uv.len())) | |
| .fold(0usize, usize::saturating_add), | |
| ); | |
| if n != 0 { | |
| issues.push(Issue { | |
| lossy: false, | |
| code: "legacy_properties", | |
| count: n, | |
| }); | |
| } | |
| } | |
| } | |
| Target::Txt => { | |
| let mut n = 0usize; | |
| for names in [&world.string_properties, &world.uv_properties] { | |
| let mut seen = HashSet::new(); | |
| for name in names { | |
| if !seen.insert(name) { | |
| n = n.saturating_add(1); | |
| } | |
| } | |
| } | |
| if n != 0 { | |
| issues.push(Issue { | |
| lossy: false, | |
| code: "duplicate_names", | |
| count: n, | |
| }); | |
| } | |
| } | |
| } | |
| Report { target, issues } | |
| } | |
| mod tests { | |
| use super::*; | |
| fn grouped_reports_and_no_mutation() { | |
| let mut w = World { | |
| string_properties: vec!["é".into(), "é".into()], | |
| ..World::default() | |
| }; | |
| let original = w.clone(); | |
| assert_eq!(analyze(&w, Target::Txt).issues[0].count, 1); | |
| let r = analyze(&w, Target::Dat(Version::V201501280)); | |
| assert_eq!(r.issues.len(), 2); | |
| assert!(!r.compatible()); | |
| assert!(r.json().contains("\"schema\":1")); | |
| assert_eq!(w, original); | |
| w.string_properties.clear(); | |
| assert!(analyze(&w, Target::Txt).compatible()); | |
| } | |
| } | |