1use std::borrow::Cow;
6
7use rustc_hash::FxHashMap;
8
9use crate::codec::primitives::Writer;
10use crate::model::{DataType, Id, Op};
11
12#[derive(Debug, Clone, PartialEq)]
17pub struct Edit<'a> {
18 pub id: Id,
20 pub name: Cow<'a, str>,
22 pub authors: Vec<Id>,
24 pub created_at: i64,
26 pub ops: Vec<Op<'a>>,
28}
29
30impl<'a> Edit<'a> {
31 pub fn new(id: Id) -> Self {
33 Self {
34 id,
35 name: Cow::Borrowed(""),
36 authors: Vec::new(),
37 created_at: 0,
38 ops: Vec::new(),
39 }
40 }
41
42 pub fn with_name(id: Id, name: impl Into<Cow<'a, str>>) -> Self {
44 Self {
45 id,
46 name: name.into(),
47 authors: Vec::new(),
48 created_at: 0,
49 ops: Vec::new(),
50 }
51 }
52}
53
54#[derive(Debug, Clone, Default)]
59pub struct WireDictionaries {
60 pub properties: Vec<(Id, DataType)>,
62 pub relation_types: Vec<Id>,
64 pub languages: Vec<Id>,
66 pub units: Vec<Id>,
68 pub objects: Vec<Id>,
70}
71
72impl WireDictionaries {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn get_property(&self, index: usize) -> Option<&(Id, DataType)> {
80 self.properties.get(index)
81 }
82
83 pub fn get_relation_type(&self, index: usize) -> Option<&Id> {
85 self.relation_types.get(index)
86 }
87
88 pub fn get_language(&self, index: usize) -> Option<&Id> {
93 if index == 0 {
94 None
95 } else {
96 self.languages.get(index - 1)
97 }
98 }
99
100 pub fn get_unit(&self, index: usize) -> Option<&Id> {
105 if index == 0 {
106 None
107 } else {
108 self.units.get(index - 1)
109 }
110 }
111
112 pub fn get_object(&self, index: usize) -> Option<&Id> {
114 self.objects.get(index)
115 }
116}
117
118#[derive(Debug, Clone, Default)]
122pub struct DictionaryBuilder {
123 properties: Vec<(Id, DataType)>,
124 property_indices: FxHashMap<Id, usize>,
125 relation_types: Vec<Id>,
126 relation_type_indices: FxHashMap<Id, usize>,
127 languages: Vec<Id>,
128 language_indices: FxHashMap<Id, usize>,
129 units: Vec<Id>,
130 unit_indices: FxHashMap<Id, usize>,
131 objects: Vec<Id>,
132 object_indices: FxHashMap<Id, usize>,
133}
134
135impl DictionaryBuilder {
136 pub fn new() -> Self {
138 Self::default()
139 }
140
141 pub fn with_capacity(estimated_ops: usize) -> Self {
150 let prop_cap = estimated_ops / 4 + 1;
151 let rel_cap = estimated_ops / 20 + 1;
152 let lang_cap = 4;
153 let unit_cap = 4;
154 let obj_cap = estimated_ops / 2 + 1;
155
156 Self {
157 properties: Vec::with_capacity(prop_cap),
158 property_indices: FxHashMap::with_capacity_and_hasher(prop_cap, Default::default()),
159 relation_types: Vec::with_capacity(rel_cap),
160 relation_type_indices: FxHashMap::with_capacity_and_hasher(rel_cap, Default::default()),
161 languages: Vec::with_capacity(lang_cap),
162 language_indices: FxHashMap::with_capacity_and_hasher(lang_cap, Default::default()),
163 units: Vec::with_capacity(unit_cap),
164 unit_indices: FxHashMap::with_capacity_and_hasher(unit_cap, Default::default()),
165 objects: Vec::with_capacity(obj_cap),
166 object_indices: FxHashMap::with_capacity_and_hasher(obj_cap, Default::default()),
167 }
168 }
169
170 pub fn add_property(&mut self, id: Id, data_type: DataType) -> usize {
172 if let Some(&idx) = self.property_indices.get(&id) {
173 idx
174 } else {
175 let idx = self.properties.len();
176 self.properties.push((id, data_type));
177 self.property_indices.insert(id, idx);
178 idx
179 }
180 }
181
182 pub fn add_relation_type(&mut self, id: Id) -> usize {
184 if let Some(&idx) = self.relation_type_indices.get(&id) {
185 idx
186 } else {
187 let idx = self.relation_types.len();
188 self.relation_types.push(id);
189 self.relation_type_indices.insert(id, idx);
190 idx
191 }
192 }
193
194 pub fn add_language(&mut self, id: Option<Id>) -> usize {
198 match id {
199 None => 0,
200 Some(lang_id) => {
201 if let Some(&idx) = self.language_indices.get(&lang_id) {
202 idx + 1
203 } else {
204 let idx = self.languages.len();
205 self.languages.push(lang_id);
206 self.language_indices.insert(lang_id, idx);
207 idx + 1
208 }
209 }
210 }
211 }
212
213 pub fn add_unit(&mut self, id: Option<Id>) -> usize {
217 match id {
218 None => 0,
219 Some(unit_id) => {
220 if let Some(&idx) = self.unit_indices.get(&unit_id) {
221 idx + 1
222 } else {
223 let idx = self.units.len();
224 self.units.push(unit_id);
225 self.unit_indices.insert(unit_id, idx);
226 idx + 1
227 }
228 }
229 }
230 }
231
232 pub fn add_object(&mut self, id: Id) -> usize {
234 if let Some(&idx) = self.object_indices.get(&id) {
235 idx
236 } else {
237 let idx = self.objects.len();
238 self.objects.push(id);
239 self.object_indices.insert(id, idx);
240 idx
241 }
242 }
243
244 pub fn build(self) -> WireDictionaries {
246 WireDictionaries {
247 properties: self.properties,
248 relation_types: self.relation_types,
249 languages: self.languages,
250 units: self.units,
251 objects: self.objects,
252 }
253 }
254
255 pub fn as_wire_dicts(&self) -> WireDictionaries {
258 WireDictionaries {
259 properties: self.properties.clone(),
260 relation_types: self.relation_types.clone(),
261 languages: self.languages.clone(),
262 units: self.units.clone(),
263 objects: self.objects.clone(),
264 }
265 }
266
267 pub fn get_property_index(&self, id: &Id) -> Option<usize> {
269 self.property_indices.get(id).copied()
270 }
271
272 pub fn get_relation_type_index(&self, id: &Id) -> Option<usize> {
274 self.relation_type_indices.get(id).copied()
275 }
276
277 pub fn get_language_index(&self, id: Option<&Id>) -> Option<usize> {
280 match id {
281 None => Some(0),
282 Some(lang_id) => self.language_indices.get(lang_id).map(|idx| idx + 1),
283 }
284 }
285
286 pub fn get_object_index(&self, id: &Id) -> Option<usize> {
288 self.object_indices.get(id).copied()
289 }
290
291 pub fn write_dictionaries(&self, writer: &mut Writer) {
293 writer.write_varint(self.properties.len() as u64);
295 for (id, data_type) in &self.properties {
296 writer.write_id(id);
297 writer.write_byte(*data_type as u8);
298 }
299
300 writer.write_id_vec(&self.relation_types);
302
303 writer.write_id_vec(&self.languages);
305
306 writer.write_id_vec(&self.units);
308
309 writer.write_id_vec(&self.objects);
311 }
312
313 pub fn into_sorted(self) -> Self {
320 let mut properties = self.properties;
322 properties.sort_by(|a, b| a.0.cmp(&b.0));
323 let property_indices: FxHashMap<Id, usize> = properties
324 .iter()
325 .enumerate()
326 .map(|(i, (id, _))| (*id, i))
327 .collect();
328
329 let mut relation_types = self.relation_types;
331 relation_types.sort();
332 let relation_type_indices: FxHashMap<Id, usize> = relation_types
333 .iter()
334 .enumerate()
335 .map(|(i, id)| (*id, i))
336 .collect();
337
338 let mut languages = self.languages;
340 languages.sort();
341 let language_indices: FxHashMap<Id, usize> = languages
342 .iter()
343 .enumerate()
344 .map(|(i, id)| (*id, i))
345 .collect();
346
347 let mut units = self.units;
349 units.sort();
350 let unit_indices: FxHashMap<Id, usize> = units
351 .iter()
352 .enumerate()
353 .map(|(i, id)| (*id, i))
354 .collect();
355
356 let mut objects = self.objects;
358 objects.sort();
359 let object_indices: FxHashMap<Id, usize> = objects
360 .iter()
361 .enumerate()
362 .map(|(i, id)| (*id, i))
363 .collect();
364
365 Self {
366 properties,
367 property_indices,
368 relation_types,
369 relation_type_indices,
370 languages,
371 language_indices,
372 units,
373 unit_indices,
374 objects,
375 object_indices,
376 }
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383
384 #[test]
385 fn test_edit_new() {
386 let id = [1u8; 16];
387 let edit = Edit::new(id);
388 assert_eq!(edit.id, id);
389 assert!(edit.name.is_empty());
390 assert!(edit.authors.is_empty());
391 assert!(edit.ops.is_empty());
392 }
393
394 #[test]
395 fn test_dictionary_builder() {
396 let mut builder = DictionaryBuilder::new();
397
398 let prop1 = [1u8; 16];
399 let prop2 = [2u8; 16];
400
401 assert_eq!(builder.add_property(prop1, DataType::Text), 0);
403 assert_eq!(builder.add_property(prop1, DataType::Text), 0);
405 assert_eq!(builder.add_property(prop2, DataType::Int64), 1);
407
408 let dicts = builder.build();
409 assert_eq!(dicts.properties.len(), 2);
410 assert_eq!(dicts.properties[0], (prop1, DataType::Text));
411 assert_eq!(dicts.properties[1], (prop2, DataType::Int64));
412 }
413
414 #[test]
415 fn test_language_indexing() {
416 let mut builder = DictionaryBuilder::new();
417
418 let lang1 = [10u8; 16];
419 let lang2 = [20u8; 16];
420
421 assert_eq!(builder.add_language(None), 0);
423 assert_eq!(builder.add_language(Some(lang1)), 1);
425 assert_eq!(builder.add_language(Some(lang1)), 1);
427 assert_eq!(builder.add_language(Some(lang2)), 2);
429
430 let dicts = builder.build();
431 assert_eq!(dicts.languages.len(), 2);
432
433 assert!(dicts.get_language(0).is_none());
435 assert_eq!(dicts.get_language(1), Some(&lang1));
437 assert_eq!(dicts.get_language(2), Some(&lang2));
439 }
440}