Skip to main content

aam_core/
found_value.rs

1//! Wrapper type returned by AAML lookup methods.
2
3use crate::aaml::parsing;
4use crate::types_aam::list::ListType;
5use std::collections::HashMap;
6use std::fmt::Display;
7use std::ops::Deref;
8
9/// The result of a successful key lookup in an [`AAML`](crate::aaml::AAML) map.
10///
11/// `FoundValue` wraps the string value associated with a key and provides
12/// helper methods for common transformations.
13#[derive(Debug, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct FoundValue {
16    inner: String,
17}
18
19impl FoundValue {
20    /// Creates a new `FoundValue` from a string slice.
21    #[must_use]
22    pub fn new(value: &str) -> Self {
23        Self {
24            inner: value.to_string(),
25        }
26    }
27
28    /// Removes all occurrences of `target` from the inner string in-place.
29    ///
30    /// Returns `&mut Self` for chaining.
31    pub fn remove(&mut self, target: &str) -> &mut Self {
32        self.inner = self.inner.replace(target, "");
33        self
34    }
35
36    /// Returns the inner value as a string slice.
37    #[must_use]
38    pub fn as_str(&self) -> &str {
39        &self.inner
40    }
41
42    /// Parses the value as a list literal `[item, item, ...]` and returns
43    /// the items as a `Vec<String>`.
44    ///
45    /// Returns `None` if the value is not in `[...]` form.
46    ///
47    /// # Example
48    /// ```
49    /// use aam_core::found_value::FoundValue;
50    /// let v = FoundValue::new("[rust, aam, config]");
51    /// assert_eq!(v.as_list().unwrap(), vec!["rust", "aam", "config"]);
52    /// ```
53    #[must_use]
54    pub fn as_list(&self) -> Option<Vec<String>> {
55        ListType::parse_items(&self.inner)
56    }
57
58    /// Parses the value as an inline object `{ k = v, ... }` and returns a
59    /// `HashMap<String, String>` of its fields.
60    ///
61    /// Returns `None` if the value is not in `{...}` form or cannot be parsed.
62    ///
63    /// # Example
64    /// ```
65    /// use aam_core::found_value::FoundValue;
66    /// let v = FoundValue::new("{ x = 1.0, y = 2.0 }");
67    /// let map = v.as_object().unwrap();
68    /// assert_eq!(map["x"], "1.0");
69    /// ```
70    #[must_use]
71    pub fn as_object(&self) -> Option<HashMap<String, String>> {
72        if !parsing::is_inline_object(&self.inner) {
73            return None;
74        }
75        parsing::parse_inline_object(&self.inner)
76            .ok()
77            .map(|pairs| pairs.into_iter().collect())
78    }
79
80    /// Returns `true` when this value is a list literal `[...]`.
81    #[must_use]
82    pub fn is_list(&self) -> bool {
83        let s = self.inner.trim();
84        s.starts_with('[') && s.ends_with(']')
85    }
86
87    /// Returns `true` when this value is an inline object literal `{...}`.
88    #[must_use]
89    pub fn is_object(&self) -> bool {
90        parsing::is_inline_object(&self.inner)
91    }
92
93    /// Attempts to parse the inner value into any type that implements `FromStr`.
94    /// Suitable for u32, f32, bool, etc.
95    ///
96    /// # Errors
97    ///
98    /// Returns `FromStr`'s associated error type if parsing fails (e.g., invalid format for the target type).
99    pub fn parse<T>(&self) -> Result<T, T::Err>
100    where
101        T: std::str::FromStr,
102    {
103        self.inner.parse::<T>()
104    }
105
106    /// Specialized method for parsing "list" strings of the form [1, 2, 3]
107    /// into a vector of elements of the desired type.
108    ///
109    /// Returns `None` if this is not a list, or a `Result` with the element parsing error.
110    #[must_use]
111    pub fn parse_list<T>(&self) -> Option<Result<Vec<T>, T::Err>>
112    where
113        T: std::str::FromStr,
114    {
115        self.as_list().map(|items| {
116            items
117                .into_iter()
118                .map(|s| s.trim().parse::<T>())
119                .collect::<Result<Vec<T>, T::Err>>()
120        })
121    }
122}
123
124impl From<String> for FoundValue {
125    fn from(value: String) -> Self {
126        Self { inner: value }
127    }
128}
129
130impl PartialEq<&str> for FoundValue {
131    fn eq(&self, other: &&str) -> bool {
132        self.inner == *other
133    }
134}
135
136impl Display for FoundValue {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        f.write_str(&self.inner)
139    }
140}
141
142impl Deref for FoundValue {
143    type Target = str;
144
145    fn deref(&self) -> &Self::Target {
146        &self.inner
147    }
148}