qualia_client_core/wellfair/
body_constitution.rs1use std::path::Path;
7
8use wellfare_core::anatomy::{BodyConstitution, BodyFit, PhysiologicalState};
9
10pub const CONSTITUTION_FILE: &str = "wellfair/body_constitution.json";
12
13pub fn load(storage_root: impl AsRef<Path>) -> Option<BodyConstitution> {
14 let path = storage_root.as_ref().join(CONSTITUTION_FILE);
15 let bytes = std::fs::read(path).ok()?;
16 serde_json::from_slice(&bytes).ok()
17}
18
19pub fn save(storage_root: impl AsRef<Path>, body: &BodyConstitution) -> Result<(), String> {
20 body.validate()?;
21 let path = storage_root.as_ref().join(CONSTITUTION_FILE);
22 if let Some(parent) = path.parent() {
23 std::fs::create_dir_all(parent).map_err(|e| e.to_string())?;
24 }
25 let bytes = serde_json::to_vec_pretty(body).map_err(|e| e.to_string())?;
26 std::fs::write(path, bytes).map_err(|e| e.to_string())
27}
28
29pub fn clear(storage_root: impl AsRef<Path>) -> Result<(), String> {
30 let path = storage_root.as_ref().join(CONSTITUTION_FILE);
31 match std::fs::remove_file(path) {
32 Ok(()) => Ok(()),
33 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
34 Err(e) => Err(e.to_string()),
35 }
36}
37
38pub fn fit_for(constitution: &BodyConstitution, phys: &PhysiologicalState) -> BodyFit {
41 let pregnancy = match phys {
42 PhysiologicalState::Reproductive(wellfare_core::anatomy::ReproductiveState::Pregnant(t)) => {
43 Some(*t)
44 }
45 _ => constitution.attributes.pregnancy,
46 };
47 constitution.fit_with_pregnancy(pregnancy)
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use wellfare_core::anatomy::{Karyotype, ReproductiveState};
54
55 #[test]
56 fn persists_and_clears() {
57 let dir = tempfile::tempdir().unwrap();
58 assert!(load(dir.path()).is_none());
59 let mut c = BodyConstitution::default();
60 c.measurements.stature_mm = Some(1720);
61 c.characteristics.karyotype = Some(Karyotype::Xx);
62 c.knowledge.ethnicities.push(
63 wellfare_core::anatomy::EthnicityAffiliation::declared("Ashkenazi").unwrap(),
64 );
65 c.attributes.eye_colour = Some("brown".into());
66 c.measurements.sleeve_mm = Some(610);
67 c.measurements.foot_left_mm = Some(265);
68 save(dir.path(), &c).unwrap();
69 let back = load(dir.path()).unwrap();
70 assert_eq!(back.measurements.stature_mm, Some(1720));
71 assert_eq!(back.knowledge.ethnicities.len(), 1);
72 assert_eq!(back.knowledge.ethnicities[0].token, "ashkenazi");
73 assert_eq!(back.attributes.eye_colour.as_deref(), Some("brown"));
74 assert_eq!(back.measurements.sleeve_mm, Some(610));
75 assert_eq!(back.measurements.foot_left_mm, Some(265));
76 clear(dir.path()).unwrap();
77 assert!(load(dir.path()).is_none());
78 }
79
80 #[test]
81 fn save_rejects_invalid() {
82 let dir = tempfile::tempdir().unwrap();
83 let mut c = BodyConstitution::default();
84 c.measurements.stature_mm = Some(10);
85 assert!(save(dir.path(), &c).is_err());
86 }
87
88 #[test]
89 fn phys_pregnancy_drives_fit() {
90 let c = BodyConstitution::default();
91 let phys = PhysiologicalState::Reproductive(ReproductiveState::Pregnant(
92 wellfare_core::anatomy::Trimester::Third,
93 ));
94 let fit = fit_for(&c, &phys);
95 assert!(fit.pregnancy_abdomen > 0.3);
96 }
97}