1use std::collections::HashMap;
7
8#[derive(Debug, Clone, PartialEq)]
14pub enum AmfValue {
15 Null,
17
18 Undefined,
20
21 Boolean(bool),
23
24 Number(f64),
26
27 String(String),
29
30 Array(Vec<AmfValue>),
34
35 Object(HashMap<String, AmfValue>),
38
39 TypedObject {
41 class_name: String,
42 properties: HashMap<String, AmfValue>,
43 },
44
45 Date(f64),
48
49 Xml(String),
51
52 ByteArray(Vec<u8>),
54
55 Integer(i32),
57
58 EcmaArray(HashMap<String, AmfValue>),
61}
62
63impl AmfValue {
64 pub fn as_str(&self) -> Option<&str> {
66 match self {
67 AmfValue::String(s) => Some(s),
68 _ => None,
69 }
70 }
71
72 pub fn as_number(&self) -> Option<f64> {
74 match self {
75 AmfValue::Number(n) => Some(*n),
76 AmfValue::Integer(i) => Some(*i as f64),
77 _ => None,
78 }
79 }
80
81 pub fn as_bool(&self) -> Option<bool> {
83 match self {
84 AmfValue::Boolean(b) => Some(*b),
85 _ => None,
86 }
87 }
88
89 pub fn as_object(&self) -> Option<&HashMap<String, AmfValue>> {
91 match self {
92 AmfValue::Object(m) => Some(m),
93 AmfValue::EcmaArray(m) => Some(m),
94 AmfValue::TypedObject { properties, .. } => Some(properties),
95 _ => None,
96 }
97 }
98
99 pub fn as_object_mut(&mut self) -> Option<&mut HashMap<String, AmfValue>> {
101 match self {
102 AmfValue::Object(m) => Some(m),
103 AmfValue::EcmaArray(m) => Some(m),
104 AmfValue::TypedObject { properties, .. } => Some(properties),
105 _ => None,
106 }
107 }
108
109 pub fn as_array(&self) -> Option<&Vec<AmfValue>> {
111 match self {
112 AmfValue::Array(a) => Some(a),
113 _ => None,
114 }
115 }
116
117 pub fn is_null_or_undefined(&self) -> bool {
119 matches!(self, AmfValue::Null | AmfValue::Undefined)
120 }
121
122 pub fn get(&self, key: &str) -> Option<&AmfValue> {
124 self.as_object()?.get(key)
125 }
126
127 pub fn get_string(&self, key: &str) -> Option<&str> {
129 self.get(key)?.as_str()
130 }
131
132 pub fn get_number(&self, key: &str) -> Option<f64> {
134 self.get(key)?.as_number()
135 }
136}
137
138impl Default for AmfValue {
139 fn default() -> Self {
140 AmfValue::Null
141 }
142}
143
144impl From<bool> for AmfValue {
145 fn from(v: bool) -> Self {
146 AmfValue::Boolean(v)
147 }
148}
149
150impl From<f64> for AmfValue {
151 fn from(v: f64) -> Self {
152 AmfValue::Number(v)
153 }
154}
155
156impl From<i32> for AmfValue {
157 fn from(v: i32) -> Self {
158 AmfValue::Number(v as f64)
159 }
160}
161
162impl From<u32> for AmfValue {
163 fn from(v: u32) -> Self {
164 AmfValue::Number(v as f64)
165 }
166}
167
168impl From<String> for AmfValue {
169 fn from(v: String) -> Self {
170 AmfValue::String(v)
171 }
172}
173
174impl From<&str> for AmfValue {
175 fn from(v: &str) -> Self {
176 AmfValue::String(v.to_string())
177 }
178}
179
180impl<V: Into<AmfValue>> From<Vec<V>> for AmfValue {
181 fn from(v: Vec<V>) -> Self {
182 AmfValue::Array(v.into_iter().map(|x| x.into()).collect())
183 }
184}
185
186impl<V: Into<AmfValue>> From<HashMap<String, V>> for AmfValue {
187 fn from(v: HashMap<String, V>) -> Self {
188 AmfValue::Object(v.into_iter().map(|(k, v)| (k, v.into())).collect())
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn test_value_accessors() {
198 let s = AmfValue::String("test".into());
199 assert_eq!(s.as_str(), Some("test"));
200 assert_eq!(s.as_number(), None);
201
202 let n = AmfValue::Number(42.0);
203 assert_eq!(n.as_number(), Some(42.0));
204 assert_eq!(n.as_str(), None);
205
206 let mut obj = HashMap::new();
207 obj.insert("key".to_string(), AmfValue::String("value".into()));
208 let o = AmfValue::Object(obj);
209 assert_eq!(o.get_string("key"), Some("value"));
210 }
211
212 #[test]
213 fn test_from_conversions() {
214 let v: AmfValue = "test".into();
215 assert!(matches!(v, AmfValue::String(_)));
216
217 let v: AmfValue = 42.0.into();
218 assert!(matches!(v, AmfValue::Number(_)));
219
220 let v: AmfValue = true.into();
221 assert!(matches!(v, AmfValue::Boolean(true)));
222 }
223}