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}