mirror of
https://codeberg.org/icewind/php-literal-parser.git
synced 2026-06-03 10:34:08 +02:00
fix nested arrays with serde
This commit is contained in:
parent
9d3295daff
commit
0127eecad2
5 changed files with 230 additions and 47 deletions
116
Cargo.lock
generated
116
Cargo.lock
generated
|
|
@ -55,20 +55,20 @@ checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
|||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -197,9 +197,9 @@ checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
|
|||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
|
|
@ -308,19 +308,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.4.0"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
|
||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.13"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
|
||||
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -525,7 +525,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "php-literal-parser"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"criterion",
|
||||
|
|
@ -670,18 +670,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.214"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.214"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -926,6 +926,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
|
@ -953,6 +959,15 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
|
|
@ -977,13 +992,30 @@ dependencies = [
|
|||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
"windows_i686_gnullvm 0.53.0",
|
||||
"windows_i686_msvc 0.53.0",
|
||||
"windows_x86_64_gnu 0.53.0",
|
||||
"windows_x86_64_gnullvm 0.53.0",
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
|
|
@ -996,6 +1028,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
|
|
@ -1008,6 +1046,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
|
|
@ -1020,12 +1064,24 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
|
|
@ -1038,6 +1094,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
|
|
@ -1050,6 +1112,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
|
|
@ -1062,6 +1130,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
|
|
@ -1073,3 +1147,9 @@ name = "windows_x86_64_msvc"
|
|||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "php-literal-parser"
|
||||
description = "parser for php literals"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
|
@ -23,6 +23,7 @@ serde_derive = "1.0.214"
|
|||
miette = { version = "7.2.0", features = ["fancy"] }
|
||||
criterion = "0.5.1"
|
||||
clap = "=4.3.24"
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
|
||||
[[bench]]
|
||||
name = "parse"
|
||||
|
|
|
|||
|
|
@ -49,6 +49,15 @@ pub enum Token {
|
|||
Error,
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub(crate) fn is_valid_array_key(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Token::Integer | Token::LiteralString | Token::Bool | Token::Float | Token::Null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lex() {
|
||||
let source = r###"
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ impl<'de> Deserializer<'de> {
|
|||
}
|
||||
|
||||
fn push_peeked(&mut self, peeked: SpannedToken<'de>) {
|
||||
self.peeked.push_back(peeked)
|
||||
self.peeked.push_front(peeked)
|
||||
}
|
||||
|
||||
fn parse_unsigned<T>(&mut self) -> Result<T>
|
||||
|
|
@ -500,6 +500,7 @@ impl<'de> SeqAccess<'de> for ArrayWalker<'de, '_> {
|
|||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
let source = self.source();
|
||||
if self.done {
|
||||
return Ok(None);
|
||||
}
|
||||
|
|
@ -515,7 +516,7 @@ impl<'de> SeqAccess<'de> for ArrayWalker<'de, '_> {
|
|||
Token::SquareOpen,
|
||||
self.syntax.close_bracket(),
|
||||
],
|
||||
self.source(),
|
||||
source,
|
||||
)?;
|
||||
|
||||
if token.token == self.syntax.close_bracket() {
|
||||
|
|
@ -523,31 +524,28 @@ impl<'de> SeqAccess<'de> for ArrayWalker<'de, '_> {
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
let next = self.de.next_token().expect_token(
|
||||
&[self.syntax.close_bracket(), Token::Comma, Token::Arrow],
|
||||
self.source(),
|
||||
)?;
|
||||
let value_token = if token.token.is_valid_array_key() {
|
||||
let next = self.de.peek_token();
|
||||
|
||||
let value_token = match next.token {
|
||||
Token::Comma => token,
|
||||
Token::Arrow => {
|
||||
let span = token.span.clone();
|
||||
if next.map(|t| t.token) == Some(Token::Arrow) {
|
||||
self.de.eat_token();
|
||||
let key_span = token.span.clone();
|
||||
let key = self.de.parser.parse_array_key(token)?;
|
||||
match key {
|
||||
Key::Int(key) if key == self.next_int_key => Ok(()),
|
||||
Key::Int(_) => Err(ParseError::UnexpectedArrayKey(ArrayKeyError::new(
|
||||
ArrayKeyErrorKind::NonConsecutive,
|
||||
self.source(),
|
||||
span,
|
||||
key_span,
|
||||
))),
|
||||
_ => Err(ParseError::UnexpectedArrayKey(ArrayKeyError::new(
|
||||
ArrayKeyErrorKind::IntegerExpected,
|
||||
self.source(),
|
||||
span,
|
||||
key_span,
|
||||
))),
|
||||
}?;
|
||||
self.next_int_key += 1;
|
||||
let value = self.de.next_token().expect_token(
|
||||
self.de.next_token().expect_token(
|
||||
&[
|
||||
Token::Bool,
|
||||
Token::Integer,
|
||||
|
|
@ -558,26 +556,27 @@ impl<'de> SeqAccess<'de> for ArrayWalker<'de, '_> {
|
|||
Token::SquareOpen,
|
||||
],
|
||||
self.source(),
|
||||
)?;
|
||||
let next = self
|
||||
.de
|
||||
.next_token()
|
||||
.expect_token(&[Token::Comma, self.syntax.close_bracket()], self.source())?;
|
||||
if next.token == self.syntax.close_bracket() {
|
||||
self.done = true;
|
||||
}
|
||||
value
|
||||
}
|
||||
peeked_token if peeked_token == self.syntax.close_bracket() => {
|
||||
self.done = true;
|
||||
)?
|
||||
} else {
|
||||
token
|
||||
}
|
||||
_ => unreachable!(),
|
||||
} else {
|
||||
token
|
||||
};
|
||||
|
||||
// Deserialize an array element.
|
||||
self.de.push_peeked(value_token);
|
||||
seed.deserialize(&mut *self.de).map(Some)
|
||||
let result = seed.deserialize(&mut *self.de).map(Some)?;
|
||||
|
||||
let comma_or_end = self
|
||||
.de
|
||||
.next_token()
|
||||
.expect_token(&[Token::Comma, self.syntax.close_bracket()], source)?;
|
||||
if comma_or_end.token == self.syntax.close_bracket() {
|
||||
self.done = true;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -646,8 +645,8 @@ impl<'de> MapAccess<'de> for ArrayWalker<'de, '_> {
|
|||
// implicit key
|
||||
let key = self.next_int_key;
|
||||
self.next_int_key += 1;
|
||||
self.de.push_peeked(token);
|
||||
self.de.push_peeked(next);
|
||||
self.de.push_peeked(token);
|
||||
seed.deserialize(format!("{}", key).into_deserializer())
|
||||
.map(Some)
|
||||
}
|
||||
|
|
|
|||
94
tests/serde.rs
Normal file
94
tests/serde.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
use miette::Report;
|
||||
use php_literal_parser::from_str;
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
#[test]
|
||||
fn test_parse_struct() {
|
||||
let code = r#"
|
||||
['name' => 'Api#get', 'url' => '/api/v{version}/{fileId}', 'verb' => 'GET']
|
||||
"#;
|
||||
let result: Route = match from_str(code) {
|
||||
Ok(result) => result,
|
||||
Err(error) => {
|
||||
let error = Report::from(error);
|
||||
eprintln!("Error while parsing return literal: {error:?}");
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
assert_eq!(
|
||||
Route {
|
||||
name: "Api#get".into(),
|
||||
url: "/api/v{version}/{fileId}".into(),
|
||||
verb: "GET".into(),
|
||||
},
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_vec_of_structs() {
|
||||
let code = r#"
|
||||
[
|
||||
['name' => 'Api#get', 'url' => '/api/v{version}/{fileId}', 'verb' => 'GET'],
|
||||
]
|
||||
"#;
|
||||
let result: Vec<Route> = match from_str(code) {
|
||||
Ok(result) => result,
|
||||
Err(error) => {
|
||||
let error = Report::from(error);
|
||||
eprintln!("Error while parsing return literal: {error:?}");
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
assert_eq!(
|
||||
vec![Route {
|
||||
name: "Api#get".into(),
|
||||
url: "/api/v{version}/{fileId}".into(),
|
||||
verb: "GET".into(),
|
||||
}],
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_struct_of_vecs_of_structs() {
|
||||
let code = r#"[
|
||||
'ocs' => [
|
||||
['name' => 'Api#get', 'url' => '/api/v{version}/{fileId}', 'verb' => 'GET'],
|
||||
],
|
||||
]"#;
|
||||
let result = match from_str(code) {
|
||||
Ok(result) => result,
|
||||
Err(error) => {
|
||||
let error = Report::from(error);
|
||||
eprintln!("Error while parsing return literal: {error:?}");
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
assert_eq!(
|
||||
AppRoutes {
|
||||
routes: Vec::new(),
|
||||
ocs: vec![Route {
|
||||
name: "Api#get".into(),
|
||||
url: "/api/v{version}/{fileId}".into(),
|
||||
verb: "GET".into(),
|
||||
}]
|
||||
},
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize, PartialEq)]
|
||||
pub struct AppRoutes {
|
||||
#[serde(default)]
|
||||
routes: Vec<Route>,
|
||||
#[serde(default)]
|
||||
ocs: Vec<Route>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq)]
|
||||
pub struct Route {
|
||||
url: String,
|
||||
name: String,
|
||||
verb: String,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue