Skip to main content

hocon_rs/
index.rs

1use super::Value;
2use core::fmt::{self, Display};
3use core::ops;
4use std::collections::HashMap;
5
6/// A trait used to index into a HOCON [`Value`].
7///
8/// This trait is sealed and cannot be implemented outside this crate.
9/// Implementations are provided for:
10/// - `usize` for indexing into arrays
11/// - `str` and `String` for indexing into objects by key
12/// - `&T` where `T: Index` for convenience
13///
14/// The behavior is similar to `serde_json::value::Index`.
15pub trait Index: private::Sealed {
16    /// Attempt to index into an immutable [`Value`], returning `Some(&Value)` if
17    /// the index is valid, otherwise `None`.
18    ///
19    /// - For `usize`, this looks up an array element.
20    /// - For `str` / `String`, this looks up an object field.
21    /// - For invalid cases, returns `None`.
22    #[doc(hidden)]
23    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value>;
24
25    /// Attempt to index into a mutable [`Value`], returning `Some(&mut Value)`
26    /// if the index is valid, otherwise `None`.
27    ///
28    /// - For `usize`, this gives mutable access to an array element.
29    /// - For `str` / `String`, this gives mutable access to an object field.
30    /// - For invalid cases, returns `None`.
31    #[doc(hidden)]
32    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value>;
33
34    /// Index into a mutable [`Value`], inserting if necessary, and return
35    /// a mutable reference.
36    ///
37    /// - For `str` / `String`, if the `Value` is `Null`, it will be replaced
38    ///   with an empty object before inserting the new key.
39    /// - For `usize`, this will panic if the index is out of bounds or if the
40    ///   `Value` is not an array.
41    /// - For `str` / `String`, this will panic if the `Value` is not an object.
42    #[doc(hidden)]
43    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value;
44}
45
46impl Index for usize {
47    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
48        match v {
49            Value::Array(vec) => vec.get(*self),
50            _ => None,
51        }
52    }
53    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
54        match v {
55            Value::Array(vec) => vec.get_mut(*self),
56            _ => None,
57        }
58    }
59    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
60        match v {
61            Value::Array(vec) => {
62                let len = vec.len();
63                vec.get_mut(*self).unwrap_or_else(|| {
64                    panic!(
65                        "cannot access index {} of HOCON array of length {}",
66                        self, len
67                    )
68                })
69            }
70            _ => panic!("cannot access index {} of HOCON {}", self, Type(v)),
71        }
72    }
73}
74
75impl Index for str {
76    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
77        match v {
78            Value::Object(map) => map.get(self),
79            _ => None,
80        }
81    }
82    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
83        match v {
84            Value::Object(map) => map.get_mut(self),
85            _ => None,
86        }
87    }
88    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
89        if let Value::Null = v {
90            *v = Value::Object(HashMap::new());
91        }
92        match v {
93            Value::Object(map) => map.entry(self.to_owned()).or_insert(Value::Null),
94            _ => panic!("cannot access key {:?} in HOCON {}", self, Type(v)),
95        }
96    }
97}
98
99impl Index for String {
100    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
101        self[..].index_into(v)
102    }
103    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
104        self[..].index_into_mut(v)
105    }
106    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
107        self[..].index_or_insert(v)
108    }
109}
110
111impl<T> Index for &T
112where
113    T: ?Sized + Index,
114{
115    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
116        (**self).index_into(v)
117    }
118    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
119        (**self).index_into_mut(v)
120    }
121    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
122        (**self).index_or_insert(v)
123    }
124}
125
126// Private sealing to prevent external implementations
127mod private {
128    pub trait Sealed {}
129    impl Sealed for usize {}
130    impl Sealed for str {}
131    impl Sealed for String {}
132    impl<T> Sealed for &T where T: ?Sized + Sealed {}
133}
134
135/// Pretty-print type information for error messages.
136struct Type<'a>(&'a Value);
137
138impl<'a> Display for Type<'a> {
139    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140        match *self.0 {
141            Value::Null => f.write_str("null"),
142            Value::Boolean(_) => f.write_str("boolean"),
143            Value::Number(_) => f.write_str("number"),
144            Value::String(_) => f.write_str("string"),
145            Value::Array(_) => f.write_str("array"),
146            Value::Object(_) => f.write_str("object"),
147        }
148    }
149}
150
151impl<I> ops::Index<I> for Value
152where
153    I: Index,
154{
155    type Output = Value;
156
157    /// Immutable indexing operator (`value[index]`).
158    ///
159    /// Returns a reference to the indexed value, or `Value::Null` if the index
160    /// is not present or invalid.
161    fn index(&self, index: I) -> &Value {
162        static NULL: Value = Value::Null;
163        index.index_into(self).unwrap_or(&NULL)
164    }
165}
166
167impl<I> ops::IndexMut<I> for Value
168where
169    I: Index,
170{
171    /// Mutable indexing operator (`value[index] = ...`).
172    ///
173    /// Will insert new entries for string keys into objects.
174    /// For `usize` indices, will panic if the index is out of bounds.
175    /// Panics if the type of the value does not match the index type.
176    fn index_mut(&mut self, index: I) -> &mut Value {
177        index.index_or_insert(self)
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184    use crate::Config;
185    use std::collections::HashMap;
186
187    const CONFIG: &str = r#"
188a = {
189    b = [
190      1.0,
191      {
192        c = [
193          {mikai = 233}
194        ],
195        d = "hello",
196      },
197    ]
198  }
199        "#;
200
201    fn make_test_value() -> Value {
202        Config::parse_str(CONFIG, None).unwrap()
203    }
204
205    #[test]
206    fn test_index_str_valid_and_invalid() {
207        let value = make_test_value();
208
209        // 有效访问
210        assert_eq!(
211            value["a"]["b"][0],
212            Value::Number(serde_json::Number::from_f64(1.0).unwrap())
213        );
214        assert_eq!(value["a"]["b"][1]["d"], Value::String("hello".into()));
215        assert_eq!(
216            value["a"]["b"][1]["c"][0]["mikai"],
217            Value::Number(233.into())
218        );
219
220        // 无效访问(不存在的 key)返回 Null
221        assert_eq!(value["a"]["b"][1]["no_such_key"], Value::Null);
222
223        // 对非对象使用字符串索引,返回 Null
224        assert_eq!(value["a"]["b"][0]["not_object"], Value::Null);
225    }
226
227    #[test]
228    fn test_index_usize_valid_and_invalid() {
229        let value = make_test_value();
230
231        // 数组越界访问返回 Null
232        assert_eq!(value["a"]["b"][10], Value::Null);
233
234        // 非数组使用数字索引,返回 Null
235        assert_eq!(value["a"]["b"][1]["d"][0], Value::Null);
236    }
237
238    #[test]
239    #[should_panic(expected = "cannot access index 2 of HOCON array of length 2")]
240    fn test_index_or_insert_usize_out_of_bounds_panics() {
241        let mut value = make_test_value();
242        let _ = &mut value["a"]["b"][2]; // 超出数组长度,panic
243    }
244
245    #[test]
246    #[should_panic(expected = "cannot access index 0 of HOCON object")]
247    fn test_index_or_insert_usize_on_object_panics() {
248        let mut value = make_test_value();
249        let _ = &mut value["a"][0]; // 对 object 用数字索引
250    }
251
252    #[test]
253    #[should_panic(expected = "cannot access key")]
254    fn test_index_or_insert_str_on_array_panics() {
255        let mut value = make_test_value();
256        let _ = &mut value["a"]["b"][0]["new_key"]; // 对 number 用 str 索引
257    }
258
259    #[test]
260    fn test_index_or_insert_str_on_null_creates_object() {
261        let mut v = Value::Null;
262        v["hello"] = Value::String("world".into());
263        assert_eq!(v["hello"], Value::String("world".into()));
264    }
265
266    #[test]
267    fn test_index_string_and_reference_equivalence() {
268        let value = make_test_value();
269        let key = "a".to_string();
270        let key_ref: &str = "a";
271
272        assert_eq!(value[&key], value["a"]);
273        assert_eq!(value[&key_ref], value["a"]);
274    }
275
276    #[test]
277    fn test_index_mut_inserts_new_field() {
278        let mut value = Value::Object(HashMap::new());
279        value["new_field"] = Value::String("hi".into());
280        assert_eq!(value["new_field"], Value::String("hi".into()));
281    }
282
283    #[test]
284    fn test_type_display() {
285        let vals = vec![
286            Value::Null,
287            Value::Boolean(true),
288            Value::Number(serde_json::Number::from_f64(3.15).unwrap()),
289            Value::String("abc".into()),
290            Value::Array(vec![]),
291            Value::Object(HashMap::new()),
292        ];
293        let expected = ["null", "boolean", "number", "string", "array", "object"];
294        for (v, exp) in vals.into_iter().zip(expected) {
295            assert_eq!(format!("{}", Type(&v)), exp);
296        }
297    }
298
299    #[test]
300    fn test_usize_index_into_mut_valid_and_invalid() {
301        let mut arr = Value::Array(vec![Value::Number(1.into()), Value::Number(2.into())]);
302        // 有效访问
303        let i = 1usize;
304        assert!(i.index_into_mut(&mut arr).is_some());
305        // 非数组类型返回 None
306        let mut non_array = Value::String("not array".into());
307        assert!(i.index_into_mut(&mut non_array).is_none());
308    }
309
310    #[test]
311    fn test_str_index_into_mut_valid_and_invalid() {
312        let mut obj = Value::Object(HashMap::from([(
313            "x".to_string(),
314            Value::String("ok".into()),
315        )]));
316        let key = "x";
317        assert!(key.index_into_mut(&mut obj).is_some());
318        let mut non_obj = Value::Array(vec![]);
319        assert!(key.index_into_mut(&mut non_obj).is_none());
320    }
321
322    #[test]
323    fn test_string_index_into_mut_and_index_or_insert() {
324        let mut obj = Value::Object(HashMap::new());
325        let k = "new".to_string();
326        // index_into_mut
327        assert!(k.index_into_mut(&mut obj).is_none());
328        // index_or_insert 应插入
329        k.index_or_insert(&mut obj);
330        assert!(matches!(obj["new"], Value::Null));
331    }
332
333    #[test]
334    fn test_ref_index_into_mut_for_string() {
335        let mut obj = Value::Object(HashMap::from([("k".to_string(), Value::Number(10.into()))]));
336        let k = "k".to_string();
337        let ref_k = &k;
338        let result = ref_k.index_into_mut(&mut obj);
339        assert!(result.is_some());
340    }
341}