yini_rs/
value.rs

1//! Value types that can be stored in YINI files.
2
3use crate::error::{Error, Result};
4
5/// A value that can be stored in a YINI file.
6///
7/// Values can be strings, integers, floats, booleans, or arrays of values.
8/// This enum provides type checking and conversion methods similar to the C++ variant.
9#[derive(Debug, Clone, PartialEq)]
10pub enum Value {
11    /// A string value.
12    String(String),
13    /// An integer value.
14    Int(i64),
15    /// A floating-point value.
16    Float(f64),
17    /// A boolean value.
18    Bool(bool),
19    /// An array of values.
20    Array(Vec<Value>),
21}
22
23impl Value {
24    /// Create a new string value.
25    pub fn string(s: impl Into<String>) -> Self {
26        Self::String(s.into())
27    }
28
29    /// Create a new integer value.
30    pub fn int(i: i64) -> Self {
31        Self::Int(i)
32    }
33
34    /// Create a new float value.
35    pub fn float(f: f64) -> Self {
36        Self::Float(f)
37    }
38
39    /// Create a new boolean value.
40    pub fn bool(b: bool) -> Self {
41        Self::Bool(b)
42    }
43
44    /// Create a new array value.
45    pub fn array(arr: Vec<Value>) -> Self {
46        Self::Array(arr)
47    }
48
49    /// Check if this value is a string.
50    pub fn is_string(&self) -> bool {
51        matches!(self, Self::String(_))
52    }
53
54    /// Check if this value is an integer.
55    pub fn is_int(&self) -> bool {
56        matches!(self, Self::Int(_))
57    }
58
59    /// Check if this value is a float.
60    pub fn is_float(&self) -> bool {
61        matches!(self, Self::Float(_))
62    }
63
64    /// Check if this value is a boolean.
65    pub fn is_bool(&self) -> bool {
66        matches!(self, Self::Bool(_))
67    }
68
69    /// Check if this value is an array.
70    pub fn is_array(&self) -> bool {
71        matches!(self, Self::Array(_))
72    }
73
74    /// Convert this value to a string.
75    ///
76    /// All value types can be converted to strings:
77    /// - Strings remain as-is
78    /// - Numbers are formatted
79    /// - Booleans become "true" or "false"
80    /// - Arrays cannot be converted and will return an error
81    pub fn as_string(&self) -> Result<String> {
82        match self {
83            Self::String(s) => Ok(s.clone()),
84            Self::Int(i) => Ok(i.to_string()),
85            Self::Float(f) => Ok(f.to_string()),
86            Self::Bool(b) => Ok(if *b { "true".to_string() } else { "false".to_string() }),
87            Self::Array(_) => Err(Error::type_error("Cannot convert array to string")),
88        }
89    }
90
91    /// Convert this value to an integer.
92    pub fn as_int(&self) -> Result<i32> {
93        match self {
94            Self::Int(i) => Ok(*i as i32),
95            Self::Float(f) => Ok(*f as i32),
96            Self::String(s) => s.parse().map_err(|_| Error::type_error("Cannot convert string to int")),
97            _ => Err(Error::type_error("Cannot convert to int")),
98        }
99    }
100
101    /// Convert this value to a floating-point number.
102    pub fn as_double(&self) -> Result<f64> {
103        match self {
104            Self::Float(f) => Ok(*f),
105            Self::Int(i) => Ok(*i as f64),
106            Self::String(s) => s.parse().map_err(|_| Error::type_error("Cannot convert string to double")),
107            _ => Err(Error::type_error("Cannot convert to double")),
108        }
109    }
110
111    /// Convert this value to a boolean.
112    ///
113    /// Conversion rules:
114    /// - Booleans remain as-is
115    /// - Strings: "true", "yes", "on", "1" (case-insensitive) are true
116    /// - Integers: non-zero values are true
117    /// - Other types cannot be converted
118    pub fn as_bool(&self) -> Result<bool> {
119        match self {
120            Self::Bool(b) => Ok(*b),
121            Self::String(s) => {
122                let lower = s.to_lowercase();
123                match lower.as_str() {
124                    "true" | "yes" | "on" | "1" => Ok(true),
125                    "false" | "no" | "off" | "0" => Ok(false),
126                    _ => Err(Error::type_error("Cannot convert string to bool")),
127                }
128            }
129            Self::Int(i) => Ok(*i != 0),
130            _ => Err(Error::type_error("Cannot convert to bool")),
131        }
132    }
133
134    /// Get this value as an array.
135    pub fn as_array(&self) -> Result<Vec<Value>> {
136        match self {
137            Self::Array(arr) => Ok(arr.clone()),
138            _ => Err(Error::type_error("Value is not an array")),
139        }
140    }
141}
142
143impl Default for Value {
144    fn default() -> Self {
145        Self::String(String::new())
146    }
147}
148
149impl From<String> for Value {
150    fn from(s: String) -> Self {
151        Self::String(s)
152    }
153}
154
155impl From<&str> for Value {
156    fn from(s: &str) -> Self {
157        Self::String(s.to_string())
158    }
159}
160
161impl From<i64> for Value {
162    fn from(i: i64) -> Self {
163        Self::Int(i)
164    }
165}
166
167impl From<i32> for Value {
168    fn from(i: i32) -> Self {
169        Self::Int(i as i64)
170    }
171}
172
173impl From<f64> for Value {
174    fn from(f: f64) -> Self {
175        Self::Float(f)
176    }
177}
178
179impl From<f32> for Value {
180    fn from(f: f32) -> Self {
181        Self::Float(f as f64)
182    }
183}
184
185impl From<bool> for Value {
186    fn from(b: bool) -> Self {
187        Self::Bool(b)
188    }
189}
190
191impl From<Vec<Value>> for Value {
192    fn from(arr: Vec<Value>) -> Self {
193        Self::Array(arr)
194    }
195}