mirror of
https://codeberg.org/icewind/php-literal-parser.git
synced 2026-06-03 10:34:08 +02:00
make value easier to work with by providing index and partialeq
This commit is contained in:
parent
53907be244
commit
63376a67cf
4 changed files with 263 additions and 22 deletions
|
|
@ -11,4 +11,6 @@ repository = "https://github.com/icewind1991/php-literal-parser"
|
||||||
logos = "0.11"
|
logos = "0.11"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
source-span = "2.2"
|
source-span = "2.2"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
35
src/ast.rs
35
src/ast.rs
|
|
@ -2,25 +2,28 @@ use crate::error::UnexpectedTokenError;
|
||||||
use crate::error::{ExpectToken, InvalidArrayKeyError, ParseError, ResultExt, SpannedError};
|
use crate::error::{ExpectToken, InvalidArrayKeyError, ParseError, ResultExt, SpannedError};
|
||||||
use crate::lexer::Token;
|
use crate::lexer::Token;
|
||||||
use crate::string::{unescape_double, unescape_single, UnescapeError};
|
use crate::string::{unescape_double, unescape_single, UnescapeError};
|
||||||
|
use crate::{Key, Value};
|
||||||
use logos::{Lexer, Logos};
|
use logos::{Lexer, Logos};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum Value {
|
|
||||||
Bool(bool),
|
|
||||||
Int(i64),
|
|
||||||
Float(f64),
|
|
||||||
String(String),
|
|
||||||
Array(HashMap<Key, Value>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
||||||
pub enum Key {
|
|
||||||
Int(i64),
|
|
||||||
String(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Parse a php literal
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use php_literal_parser::{parse, Value, Key};
|
||||||
|
/// # use std::fmt::Debug;
|
||||||
|
/// # use std::error::Error;
|
||||||
|
///
|
||||||
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
/// let map = parse(r#"["foo" => true, "nested" => ['foo' => false]]"#)?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(map["foo"], true);
|
||||||
|
/// assert_eq!(map["nested"]["foo"], false);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
pub fn parse(source: &str) -> Result<Value, SpannedError<ParseError>> {
|
pub fn parse(source: &str) -> Result<Value, SpannedError<ParseError>> {
|
||||||
let mut lexer: Lexer<Token> = Token::lexer(source);
|
let mut lexer: Lexer<Token> = Token::lexer(source);
|
||||||
parse_lexer(source, &mut lexer)
|
parse_lexer(source, &mut lexer)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,12 @@ impl<'a, T: Error + Debug> SpannedError<'a, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Error + Debug + 'static> Error for SpannedError<'a, T> {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
Some(&self.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const METRICS: DefaultMetrics = DefaultMetrics::with_tab_stop(4);
|
const METRICS: DefaultMetrics = DefaultMetrics::with_tab_stop(4);
|
||||||
|
|
||||||
impl<'a, T: Error + Debug> fmt::Display for SpannedError<'a, T> {
|
impl<'a, T: Error + Debug> fmt::Display for SpannedError<'a, T> {
|
||||||
|
|
|
||||||
242
src/lib.rs
242
src/lib.rs
|
|
@ -3,13 +3,243 @@ mod error;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
mod string;
|
mod string;
|
||||||
|
|
||||||
pub use ast::{parse, Key, Value};
|
pub use ast::parse;
|
||||||
pub use error::{ParseError, SpannedError};
|
pub use error::{ParseError, SpannedError};
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::ops::Index;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
mod tests {
|
pub enum Value {
|
||||||
#[test]
|
Bool(bool),
|
||||||
fn it_works() {
|
Int(i64),
|
||||||
assert_eq!(2 + 2, 4);
|
Float(f64),
|
||||||
|
String(String),
|
||||||
|
Array(HashMap<Key, Value>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
/// Check if the value is a bool
|
||||||
|
pub fn is_bool(&self) -> bool {
|
||||||
|
matches!(self, Value::Bool(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the value is an integer
|
||||||
|
pub fn is_int(&self) -> bool {
|
||||||
|
matches!(self, Value::Int(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the value is a float
|
||||||
|
pub fn is_float(&self) -> bool {
|
||||||
|
matches!(self, Value::Float(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the value is a string
|
||||||
|
pub fn is_string(&self) -> bool {
|
||||||
|
matches!(self, Value::String(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the value is an array
|
||||||
|
pub fn is_array(&self) -> bool {
|
||||||
|
matches!(self, Value::Array(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the value into a bool if it is one
|
||||||
|
pub fn into_bool(self) -> Option<bool> {
|
||||||
|
match self {
|
||||||
|
Value::Bool(bool) => Some(bool),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the value into a int if it is one
|
||||||
|
pub fn into_int(self) -> Option<i64> {
|
||||||
|
match self {
|
||||||
|
Value::Int(int) => Some(int),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the value into a float if it is one
|
||||||
|
pub fn into_float(self) -> Option<f64> {
|
||||||
|
match self {
|
||||||
|
Value::Float(float) => Some(float),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the value into a string if it is one
|
||||||
|
pub fn into_string(self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Value::String(string) => Some(string),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the value into a hashmap if it is one
|
||||||
|
pub fn into_hashmap(self) -> Option<HashMap<Key, Value>> {
|
||||||
|
match self {
|
||||||
|
Value::Array(map) => Some(map),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq<bool> for Value {
|
||||||
|
fn eq(&self, other: &bool) -> bool {
|
||||||
|
match self {
|
||||||
|
Value::Bool(bool) => bool == other,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<i64> for Value {
|
||||||
|
fn eq(&self, other: &i64) -> bool {
|
||||||
|
match self {
|
||||||
|
Value::Int(int) => int == other,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<f64> for Value {
|
||||||
|
fn eq(&self, other: &f64) -> bool {
|
||||||
|
match self {
|
||||||
|
Value::Float(float) => float == other,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<String> for Value {
|
||||||
|
fn eq(&self, other: &String) -> bool {
|
||||||
|
match self {
|
||||||
|
Value::String(str) => str == other,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<str> for Value {
|
||||||
|
fn eq(&self, other: &str) -> bool {
|
||||||
|
match self {
|
||||||
|
Value::String(str) => str.as_str() == other,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
|
pub enum Key {
|
||||||
|
Int(i64),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Key {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
match self {
|
||||||
|
Key::Int(int) => int.hash(state),
|
||||||
|
Key::String(str) => str.hash(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for Key {
|
||||||
|
fn from(int: i64) -> Self {
|
||||||
|
Key::Int(int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Key {
|
||||||
|
fn from(str: String) -> Self {
|
||||||
|
Key::String(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Key {
|
||||||
|
/// Check if the key is an integer
|
||||||
|
pub fn is_int(&self) -> bool {
|
||||||
|
matches!(self, Key::Int(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the key is a string
|
||||||
|
pub fn is_string(&self) -> bool {
|
||||||
|
matches!(self, Key::String(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the key into a bool if it is one
|
||||||
|
pub fn into_int(self) -> Option<i64> {
|
||||||
|
match self {
|
||||||
|
Key::Int(int) => Some(int),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the key into a string if it is one
|
||||||
|
pub fn into_string(self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Key::String(string) => Some(string),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<str> for Key {
|
||||||
|
fn borrow(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Key::String(str) => str.as_str(),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Q: ?Sized> Index<&Q> for Value
|
||||||
|
where
|
||||||
|
Key: Borrow<Q>,
|
||||||
|
Q: Eq + Hash,
|
||||||
|
{
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn index(&self, index: &Q) -> &Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Array(map) => map.index(index),
|
||||||
|
_ => panic!("index into non array value"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<Key> for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn index(&self, index: Key) -> &Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Array(map) => map.index(&index),
|
||||||
|
_ => panic!("index into non array value"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<i64> for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn index(&self, index: i64) -> &Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Array(map) => map.index(&Key::Int(index)),
|
||||||
|
_ => panic!("index into non array value"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_index() {
|
||||||
|
use maplit::hashmap;
|
||||||
|
let map = Value::Array(hashmap! {
|
||||||
|
Key::String("key".to_string()) => Value::String("value".to_string()),
|
||||||
|
Key::Int(1) => Value::Bool(true),
|
||||||
|
});
|
||||||
|
assert_eq!(map["key"], Value::String("value".to_string()));
|
||||||
|
assert_eq!(map[1], Value::Bool(true));
|
||||||
|
assert_eq!(map[Key::Int(1)], Value::Bool(true));
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue