1use onestore::{ExGuid, IdStream, Object, ObjectData, PropertySets, ResolvedRevision, Value};
2
3pub fn author(object: &Object<'_>) -> ExGuid {
4 let ObjectData::Properties(bytes) = object.data else {
5 panic!("AuthorMostRecent needs a property object")
6 };
7 let fields = PropertySets::parse(bytes).unwrap();
8 let Value::References {
9 stream: IdStream::Objects,
10 compact_ids,
11 } = fields.sets[0]
12 .iter()
13 .find(|field| field.id == 0x20001d79)
14 .unwrap()
15 .value
16 else {
17 panic!("AuthorMostRecent needs one object reference")
18 };
19 let compact = u32::from_le_bytes(compact_ids.try_into().unwrap());
20 ExGuid {
21 guid: object.global_ids[&(compact >> 8)],
22 n: compact & 255,
23 }
24}
25
26/// Checks the revision context's author/time while preserving every other metadata field.
27pub fn check(before: &ResolvedRevision<'_>, after: &ResolvedRevision<'_>, name: &str) -> u64 {
28 let roots = |revision: &ResolvedRevision<'_>| {
29 revision
30 .roots
31 .iter()
32 .filter(|(kind, _)| **kind != 4)
33 .map(|(kind, id)| (*kind, *id))
34 .collect::<Vec<_>>()
35 };
36 assert_eq!(roots(before), roots(after));
37 let id = after.roots[&4];
38 let metadata = &after.objects[&id];
39 assert_eq!(metadata.jcid, 0x20044);
40 let ObjectData::Properties(bytes) = metadata.data else {
41 panic!()
42 };
43 let fields = PropertySets::parse(bytes).unwrap();
44 let Value::Bytes(time) = fields.sets[0]
45 .iter()
46 .find(|field| field.id == 0x18001d77)
47 .unwrap()
48 .value
49 else {
50 panic!()
51 };
52 let filetime = u64::from_le_bytes(time.try_into().unwrap());
53 assert!(filetime >= 119_600_064_000_000_000);
54 let author_id = author(metadata);
55 let named = &after.objects[&author_id];
56 assert_eq!(named.jcid, 0x120001);
57 let ObjectData::Properties(bytes) = named.data else {
58 panic!()
59 };
60 let named_fields = PropertySets::parse(bytes).unwrap();
61 let Value::Bytes(stored) = named_fields.sets[0]
62 .iter()
63 .find(|field| field.id == 0x1c001d75)
64 .unwrap()
65 .value
66 else {
67 panic!()
68 };
69 let expected: Vec<_> = name
70 .encode_utf16()
71 .chain([0])
72 .flat_map(u16::to_le_bytes)
73 .collect();
74 assert_eq!(stored, expected);
75 if let Some(old) = before.objects.get(&author_id) {
76 assert_eq!(old.data, named.data);
77 }
78 if let Some(old_id) = before.roots.get(&4) {
79 assert_eq!(*old_id, id);
80 let old = &before.objects[old_id];
81 assert_eq!(old.jcid, metadata.jcid);
82 assert!(metadata.global_ids.iter().all(|(entry, guid)| {
83 old.global_ids.get(entry) == Some(guid) || *guid == author_id.guid
84 }));
85 let ObjectData::Properties(old_bytes) = old.data else {
86 panic!()
87 };
88 let mut old_fields = PropertySets::parse(old_bytes).unwrap();
89 let mut new = fields;
90 for fields in [&mut old_fields, &mut new] {
91 fields.sets[0].retain(|field| ![0x18001d77, 0x20001d79].contains(&field.id));
92 }
93 assert_eq!(old_fields.sets, new.sets);
94 for field in old_fields.sets.iter().flatten() {
95 if let Value::References { compact_ids, .. } = field.value {
96 let (ids, remainder) = compact_ids.as_chunks::<4>();
97 assert!(remainder.is_empty());
98 for compact in ids {
99 let entry = u32::from_le_bytes(*compact) >> 8;
100 assert_eq!(
101 metadata.global_ids.get(&entry),
102 Some(&old.global_ids[&entry])
103 );
104 }
105 }
106 }
107 } else {
108 assert!(!before.objects.contains_key(&id));
109 assert_eq!(fields.sets.len(), 1);
110 assert_eq!(
111 fields.sets[0]
112 .iter()
113 .map(|field| field.id)
114 .collect::<Vec<_>>(),
115 [0x18001d77, 0x20001d79]
116 );
117 }
118 filetime
119}