php_literal_parser/
lib.rs

1//! Parser for php literals.
2//!
3//! Allows parsing of php string, bool, number and array literals.
4//!
5//! ## Usage
6//!
7//! Parse into a generic representation
8//!
9//! ```rust
10//! use php_literal_parser::{from_str, Value};
11//! # use std::fmt::Debug;
12//! # use std::error::Error;
13//!
14//! # fn main() -> Result<(), Box<dyn Error>> {
15//! let map = from_str::<Value>(r#"["foo" => true, "nested" => ['foo' => false]]"#)?;
16//!
17//! assert_eq!(map["foo"], true);
18//! assert_eq!(map["nested"]["foo"], false);
19//! # Ok(())
20//! # }
21//! ```
22//!
23//! Or parse into a specific struct using serde
24//!
25//! ```rust
26//! use php_literal_parser::from_str;
27//! use serde::Deserialize;
28//! # use std::fmt::Debug;
29//! # use std::error::Error;
30//!
31//! #[derive(Debug, Deserialize, PartialEq)]
32//! struct Target {
33//!     foo: bool,
34//!     bars: Vec<u8>
35//! }
36//!
37//! # fn main() -> Result<(), Box<dyn Error>> {
38//! let target = from_str(r#"["foo" => true, "bars" => [1, 2, 3, 4,]]"#)?;
39//!
40//! assert_eq!(Target {
41//!     foo: true,
42//!     bars: vec![1,2,3,4]
43//! }, target);
44//! # Ok(())
45//! # }
46//! ```
47//!
48#![forbid(unsafe_code)]
49mod error;
50mod lexer;
51mod num;
52mod parser;
53mod serde_impl;
54mod string;
55
56use crate::string::is_array_key_numeric;
57pub use error::ParseError;
58use serde::de::{self, MapAccess, SeqAccess, Visitor};
59use serde::{Deserialize, Deserializer};
60pub use serde_impl::from_str;
61use std::borrow::Borrow;
62use std::cmp::Ordering;
63use std::collections::HashMap;
64use std::convert::TryInto;
65use std::fmt::{Display, Formatter};
66use std::hash::{Hash, Hasher};
67use std::ops::Index;
68
69/// A php value, can be either a bool, int, float, string, an array or null
70///
71/// note that in php all arrays are associative and thus represented by a map in rust.
72///
73/// You can convert a `Value` into a regular rust type by pattern matching or using the `into_` functions.
74///
75/// ## Indexing
76///
77/// If the value is a php array, you can directly index into the `Value`, this will null if the `Value` is not an array
78/// or the key is not found
79///
80/// ```rust
81/// # use maplit::hashmap;
82/// # use php_literal_parser::Value;
83/// #
84/// # fn main() {
85/// let value = Value::Array(hashmap!{
86///     "key".into() => "value".into(),
87///     10.into() => false.into()
88/// });
89/// assert_eq!(value["key"], "value");
90/// assert_eq!(value[10], false);
91/// assert!(value["not"]["found"].is_null());
92/// # }
93/// ```
94#[derive(Debug, PartialEq, Clone)]
95pub enum Value {
96    Bool(bool),
97    Int(i64),
98    Float(f64),
99    String(String),
100    Array(HashMap<Key, Value>),
101    Null,
102}
103
104impl Value {
105    /// Check if the value is a bool
106    pub fn is_bool(&self) -> bool {
107        matches!(self, Value::Bool(_))
108    }
109
110    /// Check if the value is an integer
111    pub fn is_int(&self) -> bool {
112        matches!(self, Value::Int(_))
113    }
114
115    /// Check if the value is a float
116    pub fn is_float(&self) -> bool {
117        matches!(self, Value::Float(_))
118    }
119
120    /// Check if the value is a string
121    pub fn is_string(&self) -> bool {
122        matches!(self, Value::String(_))
123    }
124
125    /// Check if the value is an array
126    pub fn is_array(&self) -> bool {
127        matches!(self, Value::Array(_))
128    }
129
130    /// Check if the value is null
131    pub fn is_null(&self) -> bool {
132        matches!(self, Value::Null)
133    }
134
135    /// Convert the value into a bool if it is one
136    pub fn into_bool(self) -> Option<bool> {
137        match self {
138            Value::Bool(bool) => Some(bool),
139            _ => None,
140        }
141    }
142
143    /// Convert the value into a int if it is one
144    pub fn into_int(self) -> Option<i64> {
145        match self {
146            Value::Int(int) => Some(int),
147            _ => None,
148        }
149    }
150
151    /// Convert the value into a float if it is one
152    pub fn into_float(self) -> Option<f64> {
153        match self {
154            Value::Float(float) => Some(float),
155            _ => None,
156        }
157    }
158
159    /// Convert the value into a string if it is one
160    pub fn into_string(self) -> Option<String> {
161        match self {
162            Value::String(string) => Some(string),
163            _ => None,
164        }
165    }
166
167    /// Convert the value into a hashmap if it is one
168    pub fn into_hashmap(self) -> Option<HashMap<Key, Value>> {
169        match self {
170            Value::Array(map) => Some(map),
171            _ => None,
172        }
173    }
174
175    /// Get the value as &str if it is a string
176    pub fn as_str(&self) -> Option<&str> {
177        match self {
178            Value::String(str) => Some(str.as_str()),
179            _ => None,
180        }
181    }
182
183    /// Get the value as i64 if it is an int
184    pub fn as_int(&self) -> Option<i64> {
185        match self {
186            Value::Int(int) => Some(*int),
187            _ => None,
188        }
189    }
190
191    /// Get the value as f64 if it is a float
192    pub fn as_float(&self) -> Option<f64> {
193        match self {
194            Value::Float(float) => Some(*float),
195            _ => None,
196        }
197    }
198
199    /// Iterate over array key and value pairs if it is an array
200    pub fn iter(&self) -> impl Iterator<Item = (&Key, &Value)> {
201        let map = match self {
202            Value::Array(map) => Some(map),
203            _ => None,
204        };
205        map.into_iter().flat_map(|map| map.iter())
206    }
207
208    /// Iterate over array keys if it is an array
209    pub fn keys(&self) -> impl Iterator<Item = &Key> {
210        self.iter().map(|(key, _value)| key)
211    }
212
213    /// Iterate over array values if it is an array
214    pub fn values(&self) -> impl Iterator<Item = &Value> {
215        self.iter().map(|(_key, value)| value)
216    }
217}
218
219impl PartialEq<bool> for Value {
220    fn eq(&self, other: &bool) -> bool {
221        match self {
222            Value::Bool(bool) => bool == other,
223            _ => false,
224        }
225    }
226}
227
228impl PartialEq<i64> for Value {
229    fn eq(&self, other: &i64) -> bool {
230        match self {
231            Value::Int(int) => int == other,
232            _ => false,
233        }
234    }
235}
236
237impl PartialEq<f64> for Value {
238    fn eq(&self, other: &f64) -> bool {
239        match self {
240            Value::Float(float) => float == other,
241            _ => false,
242        }
243    }
244}
245
246impl PartialEq<String> for Value {
247    fn eq(&self, other: &String) -> bool {
248        match self {
249            Value::String(str) => str == other,
250            _ => false,
251        }
252    }
253}
254
255impl PartialEq<&str> for Value {
256    fn eq(&self, other: &&str) -> bool {
257        match self {
258            Value::String(str) => str.as_str() == *other,
259            _ => false,
260        }
261    }
262}
263
264impl From<bool> for Value {
265    fn from(value: bool) -> Self {
266        Value::Bool(value)
267    }
268}
269
270impl From<i64> for Value {
271    fn from(value: i64) -> Self {
272        Value::Int(value)
273    }
274}
275
276impl From<f64> for Value {
277    fn from(value: f64) -> Self {
278        Value::Float(value)
279    }
280}
281
282impl From<String> for Value {
283    fn from(value: String) -> Self {
284        Value::String(value)
285    }
286}
287
288impl From<&str> for Value {
289    fn from(value: &str) -> Self {
290        Value::String(value.into())
291    }
292}
293
294impl From<HashMap<Key, Value>> for Value {
295    fn from(value: HashMap<Key, Value>) -> Self {
296        Value::Array(value)
297    }
298}
299
300impl Display for Value {
301    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
302        match self {
303            Value::Bool(val) => write!(f, "{}", val),
304            Value::Int(val) => write!(f, "{}", val),
305            Value::Float(val) => write!(f, "{}", val),
306            Value::String(val) => write!(f, "{}", val),
307            Value::Array(val) => {
308                writeln!(f, "[")?;
309                for (key, value) in val.iter() {
310                    write!(f, "\t{} => {},", key, value)?;
311                }
312                write!(f, "]")
313            }
314            Value::Null => write!(f, "null"),
315        }
316    }
317}
318
319/// A php array key, can be either an int or string
320#[derive(Debug, Eq, Clone)]
321pub enum Key {
322    Int(i64),
323    String(String),
324}
325
326impl Hash for Key {
327    fn hash<H: Hasher>(&self, state: &mut H) {
328        match self {
329            Key::Int(int) => int.hash(state),
330            Key::String(str) => str.hash(state),
331        }
332    }
333}
334
335impl From<i64> for Key {
336    fn from(int: i64) -> Self {
337        Key::Int(int)
338    }
339}
340
341impl From<String> for Key {
342    fn from(str: String) -> Self {
343        Key::String(str)
344    }
345}
346
347impl From<&str> for Key {
348    fn from(str: &str) -> Self {
349        Key::String(str.into())
350    }
351}
352
353impl Key {
354    /// Check if the key is an integer
355    pub fn is_int(&self) -> bool {
356        matches!(self, Key::Int(_))
357    }
358
359    /// Check if the key is a string
360    pub fn is_string(&self) -> bool {
361        matches!(self, Key::String(_))
362    }
363
364    /// Convert the key into a bool if it is one
365    pub fn into_int(self) -> Option<i64> {
366        match self {
367            Key::Int(int) => Some(int),
368            _ => None,
369        }
370    }
371
372    /// Convert the key into a string if it is one
373    pub fn into_string(self) -> Option<String> {
374        match self {
375            Key::String(string) => Some(string),
376            _ => None,
377        }
378    }
379
380    /// Get the key as &str if it is a string
381    pub fn as_str(&self) -> Option<&str> {
382        match self {
383            Key::String(str) => Some(str.as_str()),
384            _ => None,
385        }
386    }
387
388    /// Get the key as i64 if it is an int
389    pub fn as_int(&self) -> Option<i64> {
390        match self {
391            Key::Int(int) => Some(*int),
392            _ => None,
393        }
394    }
395}
396
397impl PartialOrd for Key {
398    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
399        Some(self.cmp(other))
400    }
401}
402
403impl Ord for Key {
404    fn cmp(&self, other: &Self) -> Ordering {
405        match (self, other) {
406            (Key::Int(self_int), Key::Int(other_int)) => self_int.cmp(other_int),
407            (Key::String(self_string), Key::String(other_string)) => self_string.cmp(other_string),
408            (Key::String(self_string), Key::Int(other_int)) => {
409                self_string.cmp(&other_int.to_string())
410            }
411            (Key::Int(self_int), Key::String(other_string)) => {
412                self_int.to_string().cmp(other_string)
413            }
414        }
415    }
416}
417
418impl PartialEq for Key {
419    fn eq(&self, other: &Self) -> bool {
420        match (self, other) {
421            (Key::Int(self_int), Key::Int(other_int)) => self_int.eq(other_int),
422            (Key::String(self_string), Key::String(other_string)) => self_string.eq(other_string),
423            (Key::String(self_string), Key::Int(other_int)) => {
424                self_string.eq(&other_int.to_string())
425            }
426            (Key::Int(self_int), Key::String(other_string)) => {
427                self_int.to_string().eq(other_string)
428            }
429        }
430    }
431}
432
433impl Borrow<str> for Key {
434    fn borrow(&self) -> &str {
435        match self {
436            Key::String(str) => str.as_str(),
437            _ => panic!(),
438        }
439    }
440}
441
442impl<Q: ?Sized> Index<&Q> for Value
443where
444    Key: Borrow<Q>,
445    Q: Eq + Hash,
446{
447    type Output = Value;
448
449    fn index(&self, index: &Q) -> &Self::Output {
450        match self {
451            Value::Array(map) => map.get(index).unwrap_or(&Value::Null),
452            _ => &Value::Null,
453        }
454    }
455}
456
457impl Index<Key> for Value {
458    type Output = Value;
459
460    fn index(&self, index: Key) -> &Self::Output {
461        match self {
462            Value::Array(map) => map.get(&index).unwrap_or(&Value::Null),
463            _ => &Value::Null,
464        }
465    }
466}
467
468impl Index<i64> for Value {
469    type Output = Value;
470
471    fn index(&self, index: i64) -> &Self::Output {
472        match self {
473            Value::Array(map) => map.get(&Key::Int(index)).unwrap_or(&Value::Null),
474            _ => &Value::Null,
475        }
476    }
477}
478
479impl Display for Key {
480    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
481        match self {
482            Key::Int(val) => write!(f, "{}", val),
483            Key::String(val) => write!(f, "{}", val),
484        }
485    }
486}
487
488#[test]
489fn test_index() {
490    use maplit::hashmap;
491    let map = Value::Array(hashmap! {
492        Key::String("key".to_string()) => Value::String("value".to_string()),
493        Key::Int(1) => Value::Bool(true),
494    });
495    assert_eq!(map["key"], "value");
496    assert_eq!(map[1], true);
497    assert_eq!(map[Key::Int(1)], true);
498}
499
500struct ValueVisitor;
501
502impl<'de> Visitor<'de> for ValueVisitor {
503    type Value = Value;
504
505    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
506        formatter.write_str("any php literal")
507    }
508
509    fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
510    where
511        E: de::Error,
512    {
513        Ok(Value::Bool(v))
514    }
515
516    fn visit_i8<E>(self, v: i8) -> Result<Self::Value, E>
517    where
518        E: de::Error,
519    {
520        Ok(Value::Int(v.into()))
521    }
522
523    fn visit_i16<E>(self, v: i16) -> Result<Self::Value, E>
524    where
525        E: de::Error,
526    {
527        Ok(Value::Int(v.into()))
528    }
529
530    fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
531    where
532        E: de::Error,
533    {
534        Ok(Value::Int(v.into()))
535    }
536
537    fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
538    where
539        E: de::Error,
540    {
541        Ok(Value::Int(v))
542    }
543
544    fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
545    where
546        E: de::Error,
547    {
548        Ok(Value::Int(v.into()))
549    }
550
551    fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
552    where
553        E: de::Error,
554    {
555        Ok(Value::Int(v.into()))
556    }
557
558    fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
559    where
560        E: de::Error,
561    {
562        Ok(Value::Int(v.into()))
563    }
564
565    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
566    where
567        E: de::Error,
568    {
569        Ok(Value::Int(v.try_into().map_err(|_| {
570            E::custom(format!("i64 out of range: {}", v))
571        })?))
572    }
573
574    fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
575    where
576        E: de::Error,
577    {
578        Ok(Value::Float(v.into()))
579    }
580
581    fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
582    where
583        E: de::Error,
584    {
585        Ok(Value::Float(v))
586    }
587
588    fn visit_char<E>(self, v: char) -> Result<Self::Value, E>
589    where
590        E: de::Error,
591    {
592        Ok(Value::String(v.to_string()))
593    }
594
595    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
596    where
597        E: de::Error,
598    {
599        Ok(Value::String(v.into()))
600    }
601
602    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
603    where
604        E: de::Error,
605    {
606        Ok(Value::String(v.into()))
607    }
608
609    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
610    where
611        E: de::Error,
612    {
613        Ok(Value::String(v))
614    }
615
616    fn visit_unit<E>(self) -> Result<Self::Value, E>
617    where
618        E: de::Error,
619    {
620        Ok(Value::Null)
621    }
622
623    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, <A as SeqAccess<'de>>::Error>
624    where
625        A: SeqAccess<'de>,
626    {
627        let mut result = HashMap::new();
628        let mut next_key = 0;
629        while let Some(value) = seq.next_element::<Value>()? {
630            let key = Key::Int(next_key);
631            next_key += 1;
632            result.insert(key, value);
633        }
634        Ok(Value::Array(result))
635    }
636
637    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, <A as MapAccess<'de>>::Error>
638    where
639        A: MapAccess<'de>,
640    {
641        let mut result = HashMap::new();
642        while let Some((key, value)) = map.next_entry()? {
643            result.insert(key, value);
644        }
645        Ok(Value::Array(result))
646    }
647}
648
649impl<'de> Deserialize<'de> for Value {
650    fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
651    where
652        D: Deserializer<'de>,
653    {
654        deserializer.deserialize_any(ValueVisitor)
655    }
656}
657
658struct KeyVisitor;
659
660impl<'de> Visitor<'de> for KeyVisitor {
661    type Value = Key;
662
663    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
664        formatter.write_str("a string, number, bool or null")
665    }
666
667    fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
668    where
669        E: de::Error,
670    {
671        Ok(Key::Int(if v { 1 } else { 0 }))
672    }
673
674    fn visit_i8<E>(self, v: i8) -> Result<Self::Value, E>
675    where
676        E: de::Error,
677    {
678        Ok(Key::Int(v.into()))
679    }
680
681    fn visit_i16<E>(self, v: i16) -> Result<Self::Value, E>
682    where
683        E: de::Error,
684    {
685        Ok(Key::Int(v.into()))
686    }
687
688    fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
689    where
690        E: de::Error,
691    {
692        Ok(Key::Int(v.into()))
693    }
694
695    fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
696    where
697        E: de::Error,
698    {
699        Ok(Key::Int(v))
700    }
701
702    fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
703    where
704        E: de::Error,
705    {
706        Ok(Key::Int(v.into()))
707    }
708
709    fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
710    where
711        E: de::Error,
712    {
713        Ok(Key::Int(v.into()))
714    }
715
716    fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
717    where
718        E: de::Error,
719    {
720        Ok(Key::Int(v.into()))
721    }
722
723    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
724    where
725        E: de::Error,
726    {
727        Ok(Key::Int(v.try_into().map_err(|_| {
728            E::custom(format!("i64 out of range: {}", v))
729        })?))
730    }
731
732    fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
733    where
734        E: de::Error,
735    {
736        Ok(Key::Int(v as i64))
737    }
738
739    fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
740    where
741        E: de::Error,
742    {
743        Ok(Key::Int(v as i64))
744    }
745
746    fn visit_char<E>(self, v: char) -> Result<Self::Value, E>
747    where
748        E: de::Error,
749    {
750        Ok(Key::String(v.to_string()))
751    }
752
753    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
754    where
755        E: de::Error,
756    {
757        self.visit_string(v.into())
758    }
759
760    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
761    where
762        E: de::Error,
763    {
764        self.visit_string(v.into())
765    }
766
767    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
768    where
769        E: de::Error,
770    {
771        if is_array_key_numeric(&v) {
772            Ok(Key::Int(v.parse().unwrap()))
773        } else {
774            Ok(Key::String(v))
775        }
776    }
777
778    fn visit_unit<E>(self) -> Result<Self::Value, E>
779    where
780        E: de::Error,
781    {
782        Ok(Key::String(String::from("")))
783    }
784}
785
786impl<'de> Deserialize<'de> for Key {
787    fn deserialize<D>(deserializer: D) -> Result<Key, D::Error>
788    where
789        D: Deserializer<'de>,
790    {
791        deserializer.deserialize_any(KeyVisitor)
792    }
793}