Skip to main content

devscout_rs/graph/
ordered.rs

1use std::collections::HashMap;
2use std::fmt;
3use std::marker::PhantomData;
4
5use serde::de::{MapAccess, Visitor};
6use serde::ser::SerializeMap;
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9// ---------------------------------------------------------------------------
10// Insertion-ordered string-keyed map -- backs fragments.json's `{rel:
11// {mtime, fragment}}` and fragments-index.json's `{rel: mtime}`. A HashMap
12// has no defined iteration order and serde_json's default Map serializer
13// (the `preserve_order` feature is not enabled) sorts by key; neither
14// matches the required file-walk-order construction. Built on serde's own
15// SerializeMap/MapAccess traits,
16// not a hand-rolled JSON writer -- serde_json still owns the actual byte
17// encoding.
18// ---------------------------------------------------------------------------
19
20#[derive(Debug, Clone, PartialEq)]
21/// Represents `OrderedMap`.
22pub struct OrderedMap<V> {
23    entries: Vec<(String, V)>,
24    index: HashMap<String, usize>,
25}
26
27impl<V> OrderedMap<V> {
28    /// Creates an empty insertion-ordered map.
29    pub fn new() -> Self {
30        Self {
31            entries: Vec::new(),
32            index: HashMap::new(),
33        }
34    }
35
36    /// Insert, or overwrite in place when the key already exists --
37    /// preserves that key's ORIGINAL position, like assigning to an existing
38    /// key on a JSON object.
39    pub fn insert(&mut self, key: String, value: V) {
40        match self.index.get(&key) {
41            Some(&i) => self.entries[i].1 = value,
42            None => {
43                self.index.insert(key.clone(), self.entries.len());
44                self.entries.push((key, value));
45            }
46        }
47    }
48
49    /// Returns the value associated with `key`, if present.
50    pub fn get(&self, key: &str) -> Option<&V> {
51        self.index.get(key).map(|&i| &self.entries[i].1)
52    }
53
54    /// Returns the number of entries.
55    pub fn len(&self) -> usize {
56        self.entries.len()
57    }
58
59    /// Returns whether the map contains no entries.
60    pub fn is_empty(&self) -> bool {
61        self.entries.is_empty()
62    }
63
64    /// Iterates over entries in insertion order.
65    pub fn iter(&self) -> impl Iterator<Item = (&String, &V)> {
66        self.entries.iter().map(|(k, v)| (k, v))
67    }
68}
69
70impl<V> Default for OrderedMap<V> {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76impl<V: Serialize> Serialize for OrderedMap<V> {
77    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
78        let mut map = serializer.serialize_map(Some(self.entries.len()))?;
79        for (k, v) in &self.entries {
80            map.serialize_entry(k, v)?;
81        }
82        map.end()
83    }
84}
85
86impl<'de, V: Deserialize<'de>> Deserialize<'de> for OrderedMap<V> {
87    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
88        struct OrderedMapVisitor<V>(PhantomData<V>);
89
90        impl<'de, V: Deserialize<'de>> Visitor<'de> for OrderedMapVisitor<V> {
91            type Value = OrderedMap<V>;
92
93            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
94                f.write_str("a JSON object")
95            }
96
97            fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
98                let mut out = OrderedMap::new();
99                while let Some((k, v)) = map.next_entry::<String, V>()? {
100                    out.insert(k, v);
101                }
102                Ok(out)
103            }
104        }
105
106        deserializer.deserialize_map(OrderedMapVisitor(PhantomData))
107    }
108}
109
110// ---------------------------------------------------------------------------
111// `stats.ambiguous_pct` -- see module header. Stored as tenths (an i64) so
112// the "is this whole?" branch is exact integer arithmetic, never a float
113// epsilon comparison.
114// ---------------------------------------------------------------------------
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117/// Represents `Percent1`.
118pub struct Percent1(pub i64);
119
120impl Percent1 {
121    /// Returns a zero percentage value.
122    pub fn zero() -> Self {
123        Percent1(0)
124    }
125
126    /// `ambiguous_pct` as `round((ambiguous / attempts) * 1000) / 10`, or 0
127    /// when there are no attempts. Takes the pre-division inputs so the
128    /// rounding happens exactly once.
129    pub fn from_ratio(numerator: usize, denominator: usize) -> Self {
130        if denominator == 0 {
131            return Percent1::zero();
132        }
133        let raw = (numerator as f64 / denominator as f64) * 1000.0;
134        Percent1(raw.round() as i64)
135    }
136}
137
138impl Serialize for Percent1 {
139    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
140        if self.0 % 10 == 0 {
141            serializer.serialize_i64(self.0 / 10)
142        } else {
143            serializer.serialize_f64(self.0 as f64 / 10.0)
144        }
145    }
146}
147
148impl<'de> Deserialize<'de> for Percent1 {
149    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
150        let v = f64::deserialize(deserializer)?;
151        Ok(Percent1((v * 10.0).round() as i64))
152    }
153}