use crate::composition::{Composition, Envelope};
use crate::library::Library;
use crate::map::Map;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Catalog {
pub descriptors: usize,
pub described: usize,
pub with_edges: usize,
pub compositions: usize,
pub bounded: usize,
pub members: usize,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct MapCensus {
pub placements: usize,
pub owned: usize,
pub stamps: usize,
pub locations: usize,
pub stamped: usize,
}
impl MapCensus {
pub fn total(&self) -> usize {
self.placements + self.stamped
}
}
pub fn of_catalog(library: &Library, compositions: &[Composition]) -> Catalog {
Catalog {
descriptors: library.descriptors.len(),
described: library.descriptors.iter().filter(|d| !d.kind.is_empty()).count(),
with_edges: library
.descriptors
.iter()
.filter(|d| {
d.subgrid
.as_ref()
.is_some_and(|g| g.cells.iter().any(|c| c.edge.is_some()))
})
.count(),
compositions: compositions.len(),
bounded: compositions
.iter()
.filter(|c| matches!(c.envelope, Envelope::Bounded { .. }))
.count(),
members: compositions.iter().map(|c| c.members.len()).sum(),
}
}
pub fn of_map(
map: &Map,
compositions: &[Composition],
library: &Library,
) -> Result<MapCensus, String> {
let stamped = crate::composition::expand_map(map, compositions, library)?
.placements
.len();
Ok(MapCensus {
placements: map.placements.len(),
owned: map.placements.iter().filter(|p| p.owned).count(),
stamps: map.stamps.len(),
locations: map.locations.len(),
stamped,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::descriptor::{Descriptor, Extent, SubCell, Subgrid};
use crate::library::LIBRARY_VERSION;
use crate::vocab::{Vocabularies, Vocabulary};
fn lib() -> Library {
let plain = Descriptor {
id: "crate_a".to_owned(),
extent: Extent { footprint: Some((0.5, 0.5)), height: Some(0.5) },
kind: vec!["container".to_owned()],
..Default::default()
};
let tokened = Descriptor {
id: "wall".to_owned(),
extent: Extent { footprint: Some((0.5, 0.5)), height: Some(0.5) },
subgrid: Some(Subgrid {
cells: vec![SubCell {
at: (0, 0, 0),
solid: true,
edge: Some("wall".to_owned()),
}],
}),
..Default::default()
};
Library { version: LIBRARY_VERSION, note: None, descriptors: vec![plain, tokened] }
}
#[test]
fn the_edge_count_agrees_with_what_the_vocabulary_resolved() {
let library = lib();
let vocab = Vocabularies {
kind: Vocabulary::of(&[("container", "a thing that holds things")]),
edge: Vocabulary::of(&[("wall", "a solid run-face")]),
..Default::default()
};
let by_scan = of_catalog(&library, &[]).with_edges;
let by_mask = library
.descriptors
.iter()
.filter(|d| vocab.masks(d).expect("resolves").edges != 0)
.count();
assert_eq!(by_scan, by_mask, "two ways of counting tokened pieces must agree");
assert_eq!(by_scan, 1);
}
#[test]
fn counting_does_not_depend_on_order() {
let mut library = lib();
let one = of_catalog(&library, &[]);
library.descriptors.reverse();
assert_eq!(one, of_catalog(&library, &[]));
}
#[test]
fn described_counts_only_the_pieces_somebody_judged() {
let c = of_catalog(&lib(), &[]);
assert_eq!(c.descriptors, 2);
assert_eq!(c.described, 1);
}
#[test]
fn a_maps_rows_and_its_stamps_are_counted_apart() {
let mut map = Map::default();
map.placements.push(crate::map::Placed {
id: "a".to_owned(),
descriptor: "crate_a".to_owned(),
owned: true,
owned_because: Some("the only way out".to_owned()),
..Default::default()
});
map.stamps.push(crate::composition::Stamped {
id: "s1".to_owned(),
of: "station".to_owned(),
..Default::default()
});
let err = of_map(&map, &[], &lib()).expect_err("the group does not exist");
assert!(
err.contains("s1"),
"the refusal must name the stamp: {err}"
);
}
#[test]
fn a_maps_stamps_are_counted_in_the_rows_they_bring() {
let mut map = Map::default();
map.placements.push(crate::map::Placed {
id: "a".to_owned(),
descriptor: "crate_a".to_owned(),
..Default::default()
});
let comp = crate::composition::Composition {
id: "station".to_owned(),
envelope: crate::composition::Envelope::Anchored,
members: vec![crate::composition::Member {
id: "m0".to_owned(),
body: crate::composition::Body::Descriptor {
id: "crate_a".to_owned(),
tip: (0, 0),
on: None,
patch: None,
},
at: (0.0, 0.0),
yaw: 0.0,
lift: 0.0,
paint: 0,
of_fingerprint: None,
note: None,
}],
locations: Vec::new(),
note: None,
};
map.stamps.push(crate::composition::Stamped {
id: "s1".to_owned(),
of: "station".to_owned(),
..Default::default()
});
let c = of_map(&map, &[comp], &lib()).unwrap_or_else(|e| panic!("{e}"));
assert_eq!((c.placements, c.stamps, c.stamped), (1, 1, 1));
assert_eq!(c.total(), 2, "the consumer will have two rows");
}
}