From 3f3dabeb23c825c855fa04acdced3a451dafce47 Mon Sep 17 00:00:00 2001 From: jihchi Date: Fri, 7 Apr 2023 13:57:54 +0200 Subject: [PATCH] Migrate kdl to v4 and utilize query engine from kdl --- Cargo.lock | 187 ++++----------- kq/Cargo.toml | 4 +- kq/src/evaluation.rs | 165 ------------- kq/src/kdlrs.rs | 391 ------------------------------ kq/src/lib.rs | 282 ---------------------- kq/src/main.rs | 14 +- kq/src/parser.rs | 553 ------------------------------------------- 7 files changed, 49 insertions(+), 1547 deletions(-) delete mode 100644 kq/src/evaluation.rs delete mode 100644 kq/src/kdlrs.rs delete mode 100644 kq/src/lib.rs delete mode 100644 kq/src/parser.rs diff --git a/Cargo.lock b/Cargo.lock index 6b00d60..36e08e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,12 +42,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - [[package]] name = "difflib" version = "0.4.0" @@ -84,17 +78,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "indoc" version = "1.0.6" @@ -112,12 +95,12 @@ dependencies = [ [[package]] name = "kdl" -version = "3.0.0" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114071e31456ec827056ca691d141f8e96327d9d9a29140da2e6fba9a5f17b83" +checksum = "062c875482ccb676fd40c804a40e3824d4464c18c364547456d1c8e8e951ae47" dependencies = [ + "miette", "nom", - "phf", "thiserror", ] @@ -129,7 +112,7 @@ dependencies = [ "getopts", "indoc", "kdl", - "nom", + "miette", "predicates", ] @@ -151,6 +134,29 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "miette" +version = "5.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abdc09c381c9336b9f2e9bd6067a9a5290d20e2d2e2296f275456121c33ae89" +dependencies = [ + "miette-derive", + "once_cell", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8842972f23939443013dfd3720f46772b743e86f1a81d120d4b6fb090f87de1c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -183,54 +189,10 @@ dependencies = [ ] [[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros", - "phf_shared", - "proc-macro-hack", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" +name = "once_cell" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "predicates" @@ -262,81 +224,24 @@ dependencies = [ "termtree", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - [[package]] name = "proc-macro2" -version = "1.0.39" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.18" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core", -] - [[package]] name = "regex" version = "1.5.6" @@ -360,17 +265,11 @@ version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - [[package]] name = "syn" -version = "1.0.95" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", @@ -385,18 +284,18 @@ checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", @@ -423,9 +322,3 @@ checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ "libc", ] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" diff --git a/kq/Cargo.toml b/kq/Cargo.toml index 71a845d..f438f62 100644 --- a/kq/Cargo.toml +++ b/kq/Cargo.toml @@ -15,6 +15,6 @@ predicates = "2.0.3" indoc = "1.0.3" [dependencies] -kdl = "3.0.0" -nom = "7.0.0" +kdl = "4.6.0" +miette = "5.7.0" getopts = "0.2" diff --git a/kq/src/evaluation.rs b/kq/src/evaluation.rs deleted file mode 100644 index bc97f02..0000000 --- a/kq/src/evaluation.rs +++ /dev/null @@ -1,165 +0,0 @@ -use crate::Operator; -use kdl::KdlValue; - -pub(crate) fn evaluate(lhs: &KdlValue, operator: &Operator, rhs: &KdlValue) -> bool { - match operator { - Operator::Contains => contains(lhs, rhs), - Operator::EndsWith => ends_with(lhs, rhs), - Operator::Equal => equal(lhs, rhs), - Operator::GreaterThan => greater_than(lhs, rhs), - Operator::GreaterThanOrEqualTo => greater_than_or_equal_to(lhs, rhs), - Operator::LessThan => less_than(lhs, rhs), - Operator::LessThanOrEqualTo => less_than_or_equal_to(lhs, rhs), - Operator::NotEqual => not_equal(lhs, rhs), - Operator::StartsWith => starts_with(lhs, rhs), - } -} - -fn contains(lhs: &KdlValue, rhs: &KdlValue) -> bool { - match lhs { - KdlValue::String(lhs) => match rhs { - KdlValue::String(rhs) => lhs.contains(rhs), - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - KdlValue::Int(_) | KdlValue::Boolean(_) | KdlValue::Null | KdlValue::Float(_) => false, - } -} - -fn ends_with(lhs: &KdlValue, rhs: &KdlValue) -> bool { - match lhs { - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - KdlValue::String(lhs) => match rhs { - KdlValue::String(rhs) => lhs.ends_with(rhs), - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - } -} - -fn equal(lhs: &KdlValue, rhs: &KdlValue) -> bool { - match lhs { - KdlValue::Int(lhs) => match rhs { - KdlValue::Int(rhs) => lhs == rhs, - KdlValue::Float(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => { - false - } - }, - KdlValue::Float(lhs) => match rhs { - #[allow(clippy::float_cmp)] - KdlValue::Float(rhs) => lhs == rhs, - KdlValue::Int(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - KdlValue::String(lhs) => match rhs { - KdlValue::String(rhs) => lhs == rhs, - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - KdlValue::Boolean(lhs) => match rhs { - KdlValue::Boolean(rhs) => lhs == rhs, - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::String(_) | KdlValue::Null => false, - }, - KdlValue::Null => match rhs { - KdlValue::Null => true, - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::String(_) | KdlValue::Boolean(_) => { - false - } - }, - } -} - -fn greater_than(lhs: &KdlValue, rhs: &KdlValue) -> bool { - match lhs { - KdlValue::Int(lhs) => match rhs { - KdlValue::Int(rhs) => lhs > rhs, - KdlValue::Float(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => { - false - } - }, - KdlValue::Float(lhs) => match rhs { - KdlValue::Float(rhs) => lhs > rhs, - KdlValue::Int(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - } -} - -fn greater_than_or_equal_to(lhs: &KdlValue, rhs: &KdlValue) -> bool { - match lhs { - KdlValue::Int(lhs) => match rhs { - KdlValue::Int(rhs) => lhs >= rhs, - KdlValue::Float(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => { - false - } - }, - KdlValue::Float(lhs) => match rhs { - KdlValue::Float(rhs) => lhs >= rhs, - KdlValue::Int(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - } -} - -fn less_than(lhs: &KdlValue, rhs: &KdlValue) -> bool { - match lhs { - KdlValue::Int(lhs) => match rhs { - KdlValue::Int(rhs) => lhs < rhs, - KdlValue::Float(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => { - false - } - }, - KdlValue::Float(lhs) => match rhs { - KdlValue::Float(rhs) => lhs < rhs, - KdlValue::Int(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - } -} - -fn less_than_or_equal_to(lhs: &KdlValue, rhs: &KdlValue) -> bool { - match lhs { - KdlValue::Int(lhs) => match rhs { - KdlValue::Int(rhs) => lhs <= rhs, - KdlValue::Float(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => { - false - } - }, - KdlValue::Float(lhs) => match rhs { - KdlValue::Float(rhs) => lhs <= rhs, - KdlValue::Int(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - } -} - -fn not_equal(lhs: &KdlValue, rhs: &KdlValue) -> bool { - match lhs { - KdlValue::Int(lhs) => match rhs { - KdlValue::Int(rhs) => lhs != rhs, - KdlValue::Float(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => { - false - } - }, - KdlValue::Float(lhs) => match rhs { - #[allow(clippy::float_cmp)] - KdlValue::Float(rhs) => lhs != rhs, - KdlValue::Int(_) | KdlValue::String(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - KdlValue::String(lhs) => match rhs { - KdlValue::String(rhs) => lhs != rhs, - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - KdlValue::Boolean(lhs) => match rhs { - KdlValue::Boolean(rhs) => lhs != rhs, - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::String(_) | KdlValue::Null => false, - }, - KdlValue::Null => false, - } -} - -fn starts_with(lhs: &KdlValue, rhs: &KdlValue) -> bool { - match lhs { - KdlValue::String(lhs) => match rhs { - KdlValue::String(rhs) => lhs.starts_with(rhs), - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - }, - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::Boolean(_) | KdlValue::Null => false, - } -} diff --git a/kq/src/kdlrs.rs b/kq/src/kdlrs.rs deleted file mode 100644 index 1ed7a6f..0000000 --- a/kq/src/kdlrs.rs +++ /dev/null @@ -1,391 +0,0 @@ -// The code of this file is modified from https://github.com/kdl-org/kdl-rs -// (mainly from https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs) -use kdl::KdlValue; -use nom::branch::alt; -use nom::bytes::complete::{tag, take_until, take_until1, take_while_m_n}; -use nom::character::complete::{anychar, char, none_of, one_of}; -use nom::combinator::{eof, map, map_opt, map_res, not, opt, recognize, value}; -use nom::multi::{fold_many0, many0, many1, many_till}; -use nom::sequence::{delimited, preceded, terminated, tuple}; -use nom::IResult; - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L148-L151 -/// `identifier := bare_identifier | string` -/// -// fn identifier(input: &str) -> IResult<&str, String, KdlParseError<&str>> { -pub(crate) fn identifier(input: &str) -> IResult<&str, String> { - alt((string, (map(bare_identifier, String::from))))(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L124-L142 -/// `bare_identifier := ((identifier-char - digit - sign) identifier-char* | sign ((identifier-char - digit) identifier-char*)?) - keyword` -/// -// fn bare_identifier(input: &str) -> IResult<&str, &str, KdlParseError<&str>>> { -fn bare_identifier(input: &str) -> IResult<&str, &str> { - // fn left(input: &str) -> IResult<&str, (), KdlParseError<&str>> { - fn left(input: &str) -> IResult<&str, ()> { - not(keyword)(input)?; - not(one_of("0123456789"))(input)?; - not(one_of("+-"))(input)?; - let (input, _) = identifier_char(input)?; - let (input, _) = many0(identifier_char)(input)?; - Ok((input, ())) - } - // fn right(input: &str) -> IResult<&str, (), KdlParseError<&str>> { - fn right(input: &str) -> IResult<&str, ()> { - let (input, _) = one_of("+-")(input)?; - not(keyword)(input)?; - not(one_of("0123456789"))(input)?; - let (input, _) = opt(many1(identifier_char))(input)?; - Ok((input, ())) - } - recognize(alt((left, right)))(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L144-L146 -/// `string := '"' character* '"'` -/// -// fn keyword(input: &str) -> IResult<&str, String, KdlParseError<&str>> { -fn keyword(input: &str) -> IResult<&str, String> { - map(alt((tag("true"), tag("false"), tag("null"))), String::from)(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L118-L122 -/// `identifier_char := unicode - linespace - [\/(){}<>;[]=,"] -/// -// fn identifier_char(input: &str) -> IResult<&str, &str, KdlParseError<&str>> { -fn identifier_char(input: &str) -> IResult<&str, &str> { - not(linespace)(input)?; - recognize(none_of(r#"\/(){}<>;[]=,""#))(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L118-L122 -/// `linespace := newline | ws | single-line-comment` -/// -// fn linespace(input: &str) -> IResult<&str, (), KdlParseError<&str>> { -fn linespace(input: &str) -> IResult<&str, ()> { - value((), alt((newline, whitespace, single_line_comment)))(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L473-L487 -/// `newline := All line-break unicode white_space -/// -// fn newline(input: &str) -> IResult<&str, (), KdlParseError<&str>> { -fn newline(input: &str) -> IResult<&str, ()> { - value( - (), - alt(( - tag("\r\n"), - tag("\r"), - tag("\n"), - tag("\u{0085}"), - tag("\u{000C}"), - tag("\u{2028}"), - tag("\u{2029}"), - )), - )(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L437-L448 -/// `ws := bom | unicode-space | multi-line-comment` -/// -// fn whitespace(input: &str) -> IResult<&str, (), KdlParseError<&str>> { -pub(crate) fn whitespace(input: &str) -> IResult<&str, ()> { - // TODO: bom? - value( - (), - alt(( - tag("\u{FEFF}"), - unicode_space, - recognize(multi_line_comment), - )), - )(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L400-L405 -/// `single-line-comment := '//' ('\r' [^\n] | [^\r\n])* (newline | eof)` -/// -// fn single_line_comment(input: &str) -> IResult<&str, (), KdlParseError<&str>> { -fn single_line_comment(input: &str) -> IResult<&str, ()> { - let (input, _) = tag("//")(input)?; - let (input, _) = many_till(value((), anychar), alt((newline, value((), eof))))(input)?; - Ok((input, ())) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L407-L411 -/// `multi-line-comment := '/*' commented-block -/// -// fn multi_line_comment(input: &str) -> IResult<&str, &str, KdlParseError<&str>> { -fn multi_line_comment(input: &str) -> IResult<&str, &str> { - let (input, _) = tag("/*")(input)?; - commented_block(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L413-L422 -/// `commented-block := '*/' | (multi-line-comment | '*' | '/' | [^*/]+) commented-block` -/// -// fn commented_block(input: &str) -> IResult<&str, &str, KdlParseError<&str>> { -fn commented_block(input: &str) -> IResult<&str, &str> { - alt(( - tag("*/"), - terminated( - alt((multi_line_comment, take_until1("*/"), tag("*"), tag("/"))), - commented_block, - ), - ))(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L450-L471 -/// -// fn unicode_space(input: &str) -> IResult<&str, &str, KdlParseError<&str>> { -fn unicode_space(input: &str) -> IResult<&str, &str> { - alt(( - tag(" "), - tag("\t"), - tag("\u{00A0}"), - tag("\u{1680}"), - tag("\u{2000}"), - tag("\u{2001}"), - tag("\u{2002}"), - tag("\u{2003}"), - tag("\u{2004}"), - tag("\u{2005}"), - tag("\u{2006}"), - tag("\u{2007}"), - tag("\u{2008}"), - tag("\u{2009}"), - tag("\u{200A}"), - tag("\u{202F}"), - tag("\u{205F}"), - tag("\u{3000}"), - ))(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L213-L223 -/// `string := '"' character* '"'` -/// -// fn string(input: &str) -> IResult<&str, String, KdlParseError<&str>> { -fn string(input: &str) -> IResult<&str, String> { - delimited( - char('"'), - fold_many0(character, String::new, |mut acc, ch| { - acc.push(ch); - acc - }), - char('"'), - )(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L225-L228 -/// `character := '\' escape | [^\"]` -/// -// fn character(input: &str) -> IResult<&str, char, KdlParseError<&str>> { -fn character(input: &str) -> IResult<&str, char> { - alt((preceded(char('\\'), escape), none_of("\\\"")))(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L237-L247 -/// a map and its inverse of escape-sequence<->char -/// -/// (instead of building a map by phf, use a function with pattern matching) -fn escape_chars(input: char) -> Option { - match input { - '"' => Some('"'), - '\\' => Some('\\'), - '/' => Some('/'), - 'b' => Some('\u{08}'), - 'f' => Some('\u{0C}'), - 'n' => Some('\n'), - 'r' => Some('\r'), - 't' => Some('\t'), - _ => None, - } -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L249-L255 -/// `escape := ["\\/bfnrt] | 'u{' hex-digit{1, 6} '}'` -/// -// fn escape(input: &str) -> IResult<&str, char, KdlParseError<&str>> { -fn escape(input: &str) -> IResult<&str, char> { - alt(( - delimited(tag("u{"), unicode, char('}')), - map_opt(anychar, escape_chars), - ))(input) -} - -// fn unicode(input: &str) -> IResult<&str, char, KdlParseError<&str>> { -fn unicode(input: &str) -> IResult<&str, char> { - map_opt( - map_res( - take_while_m_n(1, 6, |c: char| c.is_ascii_hexdigit()), - |hex| u32::from_str_radix(hex, 16), - ), - std::char::from_u32, - )(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L180-L190 -/// `value := type-annotation? (string | raw_string | number | boolean | 'null'`) -/// -// fn node_value(input: &str) -> IResult<&str, KdlValue, KdlParseError<&str>> { -pub(crate) fn node_value(input: &str) -> IResult<&str, KdlValue> { - // let (input, _ty) = opt(type_annotation)(input)?; - alt(( - map(string, KdlValue::String), - map(raw_string, |s| KdlValue::String(s.into())), - number, - boolean, - value(KdlValue::Null, tag("null")), - ))(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L267-L278 -/// `raw-string := 'r' raw-string-hash` -/// `raw-string-hash := '#' raw-string-hash '#' | raw-string-quotes` -/// `raw-string-quotes := '"' .* '"'` -/// -// fn raw_string(input: &str) -> IResult<&str, &str, KdlParseError<&str>> { -fn raw_string(input: &str) -> IResult<&str, &str> { - let (input, _) = char('r')(input)?; - let (input, hashes) = recognize(many0(char('#')))(input)?; - let (input, _) = char('"')(input)?; - let close = format!("\"{}", hashes); - let (input, string) = take_until(&close[..])(input)?; - let (input, _) = tag(&close[..])(input)?; - Ok((input, string)) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L280-L289 -/// `number := decimal | hex | octal | binary` -/// -// fn number(input: &str) -> IResult<&str, KdlValue, KdlParseError<&str>> { -fn number(input: &str) -> IResult<&str, KdlValue> { - alt(( - map(hexadecimal, KdlValue::Int), - map(octal, KdlValue::Int), - map(binary, KdlValue::Int), - map(float, KdlValue::Float), - map(integer, KdlValue::Int), - ))(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L331-L343 -/// -// fn sign(input: &str) -> IResult<&str, i64, KdlParseError<&str>> { -fn sign(input: &str) -> IResult<&str, i64> { - let (input, sign) = opt(alt((char('+'), char('-'))))(input)?; - let mult = if let Some(sign) = sign { - if sign == '+' { - 1 - } else { - -1 - } - } else { - 1 - }; - Ok((input, mult)) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L345-L358 -/// `hex := sign? '0x' [0-9a-fA-F] [0-9a-fA-F_]*` -/// -// fn hexadecimal(input: &str) -> IResult<&str, i64, KdlParseError<&str>> { -fn hexadecimal(input: &str) -> IResult<&str, i64> { - let (input, sign) = sign(input)?; - map_res( - preceded( - alt((tag("0x"), tag("0X"))), - recognize(many1(terminated( - one_of("0123456789abcdefABCDEF"), - many0(char('_')), - ))), - ), - move |out: &str| i64::from_str_radix(&str::replace(out, "_", ""), 16).map(|x| x * sign), - )(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L360-L370 -/// -/// `octal := sign? '0o' [0-7] [0-7_]*` -// fn octal(input: &str) -> IResult<&str, i64, KdlParseError<&str>> { -fn octal(input: &str) -> IResult<&str, i64> { - let (input, sign) = sign(input)?; - map_res( - preceded( - alt((tag("0o"), tag("0O"))), - recognize(many1(terminated(one_of("01234567"), many0(char('_'))))), - ), - move |out: &str| i64::from_str_radix(&str::replace(out, "_", ""), 8).map(|x| x * sign), - )(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L372-L382 -/// -// fn binary(input: &str) -> IResult<&str, i64, KdlParseError<&str>> { -fn binary(input: &str) -> IResult<&str, i64> { - let (input, sign) = sign(input)?; - map_res( - preceded( - alt((tag("0b"), tag("0B"))), - recognize(many1(terminated(one_of("01"), many0(char('_'))))), - ), - move |out: &str| i64::from_str_radix(&str::replace(out, "_", ""), 2).map(|x| x * sign), - )(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L384-L390 -/// `boolean := 'true' | 'false'` -/// -// fn boolean(input: &str) -> IResult<&str, KdlValue, KdlParseError<&str>> { -fn boolean(input: &str) -> IResult<&str, KdlValue> { - alt(( - value(KdlValue::Boolean(true), tag("true")), - value(KdlValue::Boolean(false), tag("false")), - ))(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L291-L311 -/// ```text -/// decimal := integer ('.' [0-9]+)? exponent? -/// exponent := ('e' | 'E') integer -/// integer := sign? [1-9] [0-9_]* -/// sign := '+' | '-' -/// ``` -/// -// fn float(input: &str) -> IResult<&str, f64, KdlParseError<&str>> { -fn float(input: &str) -> IResult<&str, f64> { - map_res( - alt(( - recognize(tuple(( - integer, - opt(preceded(char('.'), integer)), - one_of("eE"), - opt(one_of("+-")), - integer, - ))), - recognize(tuple((integer, char('.'), integer))), - )), - |x| str::replace(x, "_", "").parse::(), - )(input) -} - -/// https://github.com/kdl-org/kdl-rs/blob/v3.0.0/src/parser.rs#L313-L329 -/// ```text -/// decimal := integer ('.' [0-9]+)? exponent? -/// exponent := ('e' | 'E') integer -/// integer := sign? [1-9] [0-9_]* -/// sign := '+' | '-' -/// ``` -/// -// fn integer(input: &str) -> IResult<&str, i64, KdlParseError<&str>> { -fn integer(input: &str) -> IResult<&str, i64> { - let (input, sign) = sign(input)?; - map_res( - recognize(many1(terminated(one_of("0123456789"), many0(char('_'))))), - move |out: &str| { - str::replace(out, "_", "") - .parse::() - .map(move |x| x * sign) - }, - )(input) -} diff --git a/kq/src/lib.rs b/kq/src/lib.rs deleted file mode 100644 index 137bde8..0000000 --- a/kq/src/lib.rs +++ /dev/null @@ -1,282 +0,0 @@ -use kdl::{KdlNode, KdlValue}; -use nom::combinator::all_consuming; -use nom::Finish; -use std::collections::VecDeque; -use std::iter; - -mod evaluation; -mod kdlrs; -mod parser; - -use parser::{Accessor, Combinator, Entity, Matcher, Operator, Sibling}; - -pub fn query_document(input: &str, document: Vec) -> Result, String> { - let input = input.trim(); - if input.is_empty() { - Ok(document) - } else { - all_consuming(parser::selector)(input) - .finish() - .map(|(_input, selector)| query_by_selector(selector, document)) - .map_err(|error| error.to_string()) - } -} - -fn query_by_selector(selector: Vec, document: Vec) -> Vec { - selector - .iter() - .fold( - (&Accessor::Top, document), - |(previous, document), combinator| match combinator { - Combinator::Child(accessor, siblings) => { - let is_previous_sibling_top = match previous { - Accessor::AnyElement - | Accessor::AnyElementWithTypeTag(_) - | Accessor::Closed(_, _) - | Accessor::Sole(_) => false, - Accessor::Top => true, - }; - let document = query_by_child_combinator( - is_previous_sibling_top, - accessor, - siblings, - document, - ); - (accessor, document) - } - Combinator::Descendant(accessor, siblings) => { - let document = query_by_descendant_combinator(accessor, siblings, document); - (accessor, document) - } - }, - ) - .1 -} - -fn query_by_child_combinator( - is_previous_sibling_top: bool, - accessor: &Accessor, - siblings: &[(Sibling, Accessor)], - document: Vec, -) -> Vec { - if siblings.is_empty() { - match accessor { - Accessor::AnyElement => { - if is_previous_sibling_top { - document - } else { - document - .iter() - .flat_map(|node| &node.children) - .cloned() - .collect() - } - } - Accessor::AnyElementWithTypeTag(_identifier) => vec![], - Accessor::Closed(identifier, matcher) => identifier - .as_ref() - .map(|identifier| filter_by_identifier(identifier, &document)) - .unwrap_or(document) - .iter() - .filter(|node| match_by_matcher(matcher, node)) - .cloned() - .collect(), - Accessor::Sole(identifier) => { - if is_previous_sibling_top { - filter_by_identifier(identifier, &document) - } else { - document - .iter() - .flat_map(|node| filter_by_identifier(identifier, &node.children)) - .collect() - } - } - Accessor::Top => document, - } - } else if is_previous_sibling_top { - filter_by_siblings(accessor, siblings, &document) - } else { - document - .iter() - .flat_map(|node| filter_by_siblings(accessor, siblings, &node.children)) - .collect() - } -} - -fn query_by_descendant_combinator( - accessor: &Accessor, - siblings: &[(Sibling, Accessor)], - document: Vec, -) -> Vec { - if siblings.is_empty() { - match accessor { - Accessor::AnyElement => document, - Accessor::AnyElementWithTypeTag(_identifier) => vec![], - Accessor::Closed(identifier, matcher) => traverse( - |node| match_by_accessor_filter(identifier, matcher, node), - &document, - ), - Accessor::Sole(identifier) => traverse(|node| node.name == *identifier, &document), - Accessor::Top => document, - } - } else { - traverse_by_siblings(accessor, siblings, &document) - } -} - -fn filter_by_identifier(identifier: &str, document: &[KdlNode]) -> Vec { - document - .iter() - .filter(|node| node.name == *identifier) - .cloned() - .collect() -} - -fn filter_by_siblings( - accessor: &Accessor, - siblings: &[(Sibling, Accessor)], - document: &[KdlNode], -) -> Vec { - let head = (Sibling::General, accessor.clone()); - - document - .iter() - .enumerate() - .filter(|(i, node)| { - let mut siblings = iter::once(&head).chain(siblings.iter()).rev(); - let mut preceding = document[..*i].iter().rev().peekable(); - - let result = siblings - .next() - .and_then(|(sibling, accessor)| match_by_accessor(accessor, node).then(|| sibling)); - - let result = result.map(|sibling| { - let mut previous_sibling = sibling; - - siblings.all(|(sibling, accessor)| { - let is_sibling_matched = match previous_sibling { - Sibling::Adjacent => preceding - .next() - .map(|node| match_by_accessor(accessor, node)) - .unwrap_or(false), - Sibling::General => preceding.any(|node| match_by_accessor(accessor, node)), - }; - previous_sibling = sibling; - is_sibling_matched - }) - }); - - result.unwrap_or(false) - }) - .map(|(_i, node)| node) - .cloned() - .collect() -} - -fn match_by_matcher(matcher: &Matcher, node: &KdlNode) -> bool { - match matcher { - Matcher::Direct(entity) => match entity { - Entity::PropName(name) => node.properties.contains_key(name), - Entity::Val(index) => node.values.len() > *index, - // '[name()]', '[props()]',, and '[values()]' does not make sense by themselves in a matcher - // '[tag()]' is unsupported - Entity::NodeName | Entity::Props | Entity::TypeTag | Entity::Values => false, - }, - Matcher::Expression(entity, operator, value) => match entity { - Entity::PropName(name) => node - .properties - .get(name) - .map(|lhs| evaluation::evaluate(lhs, operator, value)) - .unwrap_or(false), - Entity::Val(index) => node - .values - .get(*index) - .map(|lhs| evaluation::evaluate(lhs, operator, value)) - .unwrap_or(false), - Entity::NodeName => match value { - KdlValue::String(string) => match operator { - Operator::Contains => node.name.contains(string), - Operator::EndsWith => node.name.ends_with(string), - Operator::Equal => &node.name == string, - Operator::GreaterThan => false, - Operator::GreaterThanOrEqualTo => false, - Operator::LessThan => false, - Operator::LessThanOrEqualTo => false, - Operator::NotEqual => &node.name != string, - Operator::StartsWith => node.name.starts_with(string), - }, - KdlValue::Int(_) | KdlValue::Float(_) | KdlValue::Boolean(_) | KdlValue::Null => { - false - } - }, - Entity::Props => false, - Entity::TypeTag => false, - Entity::Values => false, - }, - } -} - -fn match_by_accessor(accessor: &Accessor, node: &KdlNode) -> bool { - match accessor { - Accessor::AnyElement => true, - Accessor::AnyElementWithTypeTag(_identifier) => false, - Accessor::Closed(identifier, matcher) => { - match_by_accessor_filter(identifier, matcher, node) - } - Accessor::Sole(identifier) => node.name == *identifier, - Accessor::Top => true, - } -} - -fn match_by_accessor_filter( - identifier: &Option, - matcher: &Matcher, - node: &KdlNode, -) -> bool { - identifier - .as_ref() - .map(|identifier| node.name == *identifier) - .unwrap_or(true) - .then(|| match_by_matcher(matcher, node)) - .unwrap_or(false) -} - -fn traverse_by_siblings( - accessor: &Accessor, - siblings: &[(Sibling, Accessor)], - document: &[KdlNode], -) -> Vec { - let mut result = Vec::::new(); - let mut queue = VecDeque::<&[KdlNode]>::new(); - queue.push_back(document); - - while let Some(document) = queue.pop_front() { - for node in filter_by_siblings(accessor, siblings, document) { - result.push(node); - } - for node in document { - if !node.children.is_empty() { - queue.push_back(&node.children); - } - } - } - - result -} - -fn traverse(predicate: F, document: &[KdlNode]) -> Vec -where - F: Fn(&KdlNode) -> bool, -{ - let mut result: Vec = vec![]; - let mut queue: VecDeque<&KdlNode> = document.iter().collect(); - - while let Some(node) = queue.pop_front() { - if predicate(node) { - result.push(node.clone()); - } - queue.extend(node.children.iter()); - } - - result -} diff --git a/kq/src/main.rs b/kq/src/main.rs index 11aa371..8973913 100644 --- a/kq/src/main.rs +++ b/kq/src/main.rs @@ -1,10 +1,10 @@ -use std::error; +use kdl::KdlDocument; use std::io::{self, Read}; mod cli; -fn main() -> Result<(), Box> { - let args = cli::Args::new()?; +fn main() -> miette::Result<()> { + let args = cli::Args::new().expect("failed to parse arguments"); if args.help() { args.print_help(); @@ -26,11 +26,11 @@ fn main() -> Result<(), Box> { let mut buffer = String::new(); let mut stdin = io::stdin(); - stdin.read_to_string(&mut buffer)?; + stdin.read_to_string(&mut buffer).expect("failed to read content from stdin"); - let nodes = kdl::parse_document(buffer)?; - let nodes = kq::query_document(query, nodes)?; - nodes.iter().for_each(|node| println!("{}", node)); + let doc = buffer.parse::()?; + let results = doc.query(query)?; + println!("{}", results); Ok(()) } diff --git a/kq/src/parser.rs b/kq/src/parser.rs deleted file mode 100644 index 0f6badc..0000000 --- a/kq/src/parser.rs +++ /dev/null @@ -1,553 +0,0 @@ -use kdl::KdlValue; -use nom::branch::alt; -use nom::bytes::complete::tag; -use nom::character::complete::digit0; -use nom::combinator::{iterator, map, opt, value}; -use nom::multi::many1; -use nom::sequence::{delimited, tuple}; -use nom::IResult; -use std::convert::TryFrom; - -use crate::kdlrs; - -#[derive(Debug, PartialEq)] -pub(crate) enum Combinator { - Child(Accessor, Vec<(Sibling, Accessor)>), - Descendant(Accessor, Vec<(Sibling, Accessor)>), -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum Sibling { - Adjacent, - General, -} - -impl TryFrom<&ParsedCombinator> for Sibling { - type Error = &'static str; - - fn try_from(value: &ParsedCombinator) -> Result { - match value { - ParsedCombinator::Child => Err("Child can not convert to Sibling"), - ParsedCombinator::Descendant => Err("Descendant can not convert to Sibling"), - ParsedCombinator::GeneralSibling => Ok(Sibling::General), - ParsedCombinator::AdjacentSibling => Ok(Sibling::Adjacent), - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum Accessor { - AnyElement, - AnyElementWithTypeTag(Option), - Closed(Option, Matcher), - Sole(String), - Top, -} - -#[derive(Clone, Debug, PartialEq)] -pub enum Matcher { - Direct(Entity), - Expression(Entity, Operator, KdlValue), -} - -#[derive(Clone, Debug, PartialEq)] -pub enum Entity { - NodeName, - PropName(String), - Props, - TypeTag, - Val(usize), - Values, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Operator { - Contains, - EndsWith, - Equal, - GreaterThan, - GreaterThanOrEqualTo, - LessThan, - LessThanOrEqualTo, - NotEqual, - StartsWith, -} - -#[derive(Debug, Clone, PartialEq)] -enum ParsedCombinator { - AdjacentSibling, - Child, - Descendant, - GeneralSibling, -} - -pub(crate) fn selector(input: &str) -> IResult<&str, Vec> { - let (input, head) = accessor(input)?; - let mut it = iterator(input, tuple((combinator, accessor))); - let tail = it.collect::>(); - it.finish()?; - - let mut output = vec![Combinator::Descendant(head, vec![])]; - let mut iter = tail.iter(); - let mut it = iter.next(); - - while it.is_some() { - // push sibling combinator onto last hierarchy combinator as a sibling - while it.is_some() && is_sibling(it) { - let (combinator, accessor) = it.unwrap(); - let combinator = Sibling::try_from(combinator).unwrap(); - let sibling = (combinator, accessor.clone()); - match output.last_mut().unwrap() { - Combinator::Child(_head, siblings) | Combinator::Descendant(_head, siblings) => { - siblings.push(sibling) - } - }; - it = iter.next(); - } - // push hierarchy combinator onto output - if let Some((combinator, accessor)) = it { - match combinator { - ParsedCombinator::Child => output.push(Combinator::Child(accessor.clone(), vec![])), - ParsedCombinator::Descendant => { - output.push(Combinator::Descendant(accessor.clone(), vec![])); - } - ParsedCombinator::GeneralSibling | ParsedCombinator::AdjacentSibling => (), - }; - it = iter.next(); - } - } - - Ok(("", output)) -} - -fn is_sibling(value: Option<&(ParsedCombinator, Accessor)>) -> bool { - match value { - Some((combinator, _accessor)) => match combinator { - ParsedCombinator::Child | ParsedCombinator::Descendant => false, - ParsedCombinator::AdjacentSibling | ParsedCombinator::GeneralSibling => true, - }, - None => false, - } -} - -fn combinator(input: &str) -> IResult<&str, ParsedCombinator> { - alt(( - delimited( - many1(kdlrs::whitespace), - alt(( - value(ParsedCombinator::Child, tag(">")), - value(ParsedCombinator::AdjacentSibling, tag("+")), - value(ParsedCombinator::GeneralSibling, tag("~")), - )), - many1(kdlrs::whitespace), - ), - value(ParsedCombinator::Descendant, many1(kdlrs::whitespace)), - ))(input) -} - -/// ```text -/// accessor := -/// 'top()' | -/// '[]' | -/// '(' identifier? ')' | -/// identifier? matcher | -/// identifier -/// ``` -fn accessor(input: &str) -> IResult<&str, Accessor> { - alt(( - value(Accessor::Top, tag("top()")), - value(Accessor::AnyElement, tag("[]")), - map( - delimited(tag("("), opt(kdlrs::identifier), tag(")")), - Accessor::AnyElementWithTypeTag, - ), - map( - tuple((opt(kdlrs::identifier), matcher)), - |(identifier, matcher)| Accessor::Closed(identifier, matcher), - ), - map(kdlrs::identifier, Accessor::Sole), - ))(input) -} - -/// `matcher := '[' entity (ws+ operator ws+ kdl-value)? ']'` -fn matcher(input: &str) -> IResult<&str, Matcher> { - let (input, _) = tag("[")(input)?; - let (input, left_hand_side) = entity(input)?; - let (input, expression) = opt(tuple(( - delimited(many1(kdlrs::whitespace), operator, many1(kdlrs::whitespace)), - kdlrs::node_value, - )))(input)?; - let (input, _) = tag("]")(input)?; - - let output = match expression { - Some((operator, right_hand_side)) => { - Matcher::Expression(left_hand_side, operator, right_hand_side) - } - None => Matcher::Direct(left_hand_side), - }; - - Ok((input, output)) -} - -/// ```text -/// entity := -/// 'name()' | -/// 'tag()' | -/// 'props()' | -/// 'values()' | -/// 'val(' digit* ')' | -/// 'prop(' identifier ')' | -/// identifier '()'? -/// ``` -fn entity(input: &str) -> IResult<&str, Entity> { - alt(( - value(Entity::NodeName, tag("name()")), - value(Entity::TypeTag, tag("tag()")), - value(Entity::Props, tag("props()")), - value(Entity::Values, tag("values()")), - map(delimited(tag("val("), digit0, tag(")")), |input: &str| { - Entity::Val(input.parse::().unwrap_or(0)) - }), - map( - delimited(tag("prop("), kdlrs::identifier, tag(")")), - Entity::PropName, - ), - map(kdlrs::identifier, Entity::PropName), - ))(input) -} - -/// `operator := '=' | '!=' | '>' | '>=' | '<' | '<=' | '^=' | '$=' | '*='` -fn operator(input: &str) -> IResult<&str, Operator> { - alt(( - value(Operator::Contains, tag("*=")), - value(Operator::EndsWith, tag("$=")), - value(Operator::GreaterThanOrEqualTo, tag(">=")), - value(Operator::LessThanOrEqualTo, tag("<=")), - value(Operator::NotEqual, tag("!=")), - value(Operator::StartsWith, tag("^=")), - value(Operator::Equal, tag("=")), - value(Operator::GreaterThan, tag(">")), - value(Operator::LessThan, tag("<")), - ))(input) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_selector() { - assert_eq!( - selector("top()"), - Ok(("", vec![Combinator::Descendant(Accessor::Top, vec![]),])) - ); - - assert_eq!( - selector("top() > []"), - Ok(( - "", - vec![ - Combinator::Descendant(Accessor::Top, vec![]), - Combinator::Child(Accessor::AnyElement, vec![]) - ] - )) - ); - - assert_eq!( - selector("top() []"), - Ok(( - "", - vec![ - Combinator::Descendant(Accessor::Top, vec![]), - Combinator::Descendant(Accessor::AnyElement, vec![]) - ] - )) - ); - - assert_eq!( - selector("a + b ~ c"), - Ok(( - "", - vec![Combinator::Descendant( - Accessor::Sole("a".to_owned()), - vec![ - (Sibling::Adjacent, Accessor::Sole("b".to_owned())), - (Sibling::General, Accessor::Sole("c".to_owned())) - ] - )] - )) - ); - - assert_eq!( - selector("a + b ~ c > []"), - Ok(( - "", - vec![ - Combinator::Descendant( - Accessor::Sole("a".to_owned()), - vec![ - (Sibling::Adjacent, Accessor::Sole("b".to_owned())), - (Sibling::General, Accessor::Sole("c".to_owned())) - ] - ), - Combinator::Child(Accessor::AnyElement, vec![]) - ] - )) - ); - - assert_eq!( - selector("a + b ~ c []"), - Ok(( - "", - vec![ - Combinator::Descendant( - Accessor::Sole("a".to_owned()), - vec![ - (Sibling::Adjacent, Accessor::Sole("b".to_owned())), - (Sibling::General, Accessor::Sole("c".to_owned())) - ] - ), - Combinator::Descendant(Accessor::AnyElement, vec![]) - ] - )) - ); - - assert_eq!( - selector("a + b ~ c d ~ e + f"), - Ok(( - "", - vec![ - Combinator::Descendant( - Accessor::Sole("a".to_owned()), - vec![ - (Sibling::Adjacent, Accessor::Sole("b".to_owned())), - (Sibling::General, Accessor::Sole("c".to_owned())) - ] - ), - Combinator::Descendant( - Accessor::Sole("d".to_owned()), - vec![ - (Sibling::General, Accessor::Sole("e".to_owned())), - (Sibling::Adjacent, Accessor::Sole("f".to_owned())) - ] - ), - ] - )) - ); - - assert_eq!( - selector("a + b ~ c > d ~ e + f"), - Ok(( - "", - vec![ - Combinator::Descendant( - Accessor::Sole("a".to_owned()), - vec![ - (Sibling::Adjacent, Accessor::Sole("b".to_owned())), - (Sibling::General, Accessor::Sole("c".to_owned())) - ] - ), - Combinator::Child( - Accessor::Sole("d".to_owned()), - vec![ - (Sibling::General, Accessor::Sole("e".to_owned())), - (Sibling::Adjacent, Accessor::Sole("f".to_owned())) - ] - ), - ] - )) - ); - - assert_eq!( - selector("top() a + b ~ c > []"), - Ok(( - "", - vec![ - Combinator::Descendant(Accessor::Top, vec![]), - Combinator::Descendant( - Accessor::Sole("a".to_owned()), - vec![ - (Sibling::Adjacent, Accessor::Sole("b".to_owned())), - (Sibling::General, Accessor::Sole("c".to_owned())) - ] - ), - Combinator::Child(Accessor::AnyElement, vec![]) - ] - )) - ); - - assert_eq!( - selector("top() > a + b ~ c []"), - Ok(( - "", - vec![ - Combinator::Descendant(Accessor::Top, vec![]), - Combinator::Child( - Accessor::Sole("a".to_owned()), - vec![ - (Sibling::Adjacent, Accessor::Sole("b".to_owned())), - (Sibling::General, Accessor::Sole("c".to_owned())) - ] - ), - Combinator::Descendant(Accessor::AnyElement, vec![]) - ] - )) - ); - } - - #[test] - fn test_combinator() { - use super::ParsedCombinator::{AdjacentSibling, Child, Descendant, GeneralSibling}; - - assert_eq!(combinator(" > "), Ok(("", Child))); - assert_eq!(combinator(" + "), Ok(("", AdjacentSibling))); - assert_eq!(combinator(" ~ "), Ok(("", GeneralSibling))); - assert_eq!(combinator(" "), Ok(("", Descendant))); - } - - #[test] - fn test_accessor() { - use super::Accessor::{AnyElement, Closed, Sole, Top}; - - assert_eq!(accessor("[]"), Ok(("", AnyElement))); - assert_eq!(accessor("name"), Ok(("", Sole("name".to_owned())))); - assert_eq!(accessor("top()"), Ok(("", Top))); - assert_eq!( - accessor("[props()]"), - Ok(("", Closed(None, Matcher::Direct(Entity::Props)))) - ); - assert_eq!( - accessor("name[props()]"), - Ok(( - "", - Closed(Some("name".to_owned()), Matcher::Direct(Entity::Props)) - )) - ); - } - - #[test] - fn test_matcher() { - use super::Matcher::{Direct, Expression}; - - assert_eq!(matcher("[name()]"), Ok(("", Direct(Entity::NodeName)))); - assert_eq!(matcher("[tag()]"), Ok(("", Direct(Entity::TypeTag)))); - assert_eq!(matcher("[props()]"), Ok(("", Direct(Entity::Props)))); - assert_eq!(matcher("[values()]"), Ok(("", Direct(Entity::Values)))); - assert_eq!(matcher("[val()]"), Ok(("", Direct(Entity::Val(0))))); - assert_eq!(matcher("[val(777)]"), Ok(("", Direct(Entity::Val(777))))); - assert_eq!( - matcher("[prop(name)]"), - Ok(("", Direct(Entity::PropName("name".to_owned())))) - ); - assert_eq!( - matcher("[prop]"), - Ok(("", Direct(Entity::PropName("prop".to_owned())))) - ); - assert!(matcher("[some()]").is_err()); - - assert_eq!( - matcher(r#"[name() = "kdl"]"#), - Ok(( - "", - Expression(Entity::NodeName, Operator::Equal, "kdl".into()) - )) - ); - assert_eq!( - matcher(r#"[tag() = "kdl"]"#), - Ok(( - "", - Expression(Entity::TypeTag, Operator::Equal, "kdl".into()) - )) - ); - assert_eq!( - matcher(r#"[props() = "kdl"]"#), - Ok(("", Expression(Entity::Props, Operator::Equal, "kdl".into()))) - ); - assert_eq!( - matcher(r#"[values() = "kdl"]"#), - Ok(( - "", - Expression(Entity::Values, Operator::Equal, "kdl".into()) - )) - ); - assert_eq!( - matcher(r#"[val() = 777]"#), - Ok(("", Expression(Entity::Val(0), Operator::Equal, 777.into()))) - ); - assert_eq!( - matcher("[val(777) = 777]"), - Ok(( - "", - Expression(Entity::Val(777), Operator::Equal, 777.into()) - )) - ); - assert_eq!( - matcher("[prop(name) = 777]"), - Ok(( - "", - Expression( - Entity::PropName("name".to_owned()), - Operator::Equal, - 777.into() - ) - )) - ); - assert_eq!( - matcher("[prop = 777]"), - Ok(( - "", - Expression( - Entity::PropName("prop".to_owned()), - Operator::Equal, - 777.into() - ) - )) - ); - assert!(matcher("[some() = 777]").is_err()); - } - - #[test] - fn test_entity() { - use super::Entity::{NodeName, PropName, Props, TypeTag, Val, Values}; - - assert_eq!(entity("name()"), Ok(("", NodeName))); - assert_eq!(entity("tag()"), Ok(("", TypeTag))); - assert_eq!(entity("props()"), Ok(("", Props))); - assert_eq!(entity("values()"), Ok(("", Values))); - assert_eq!(entity("val()"), Ok(("", Val(0)))); - assert_eq!(entity("val(777)"), Ok(("", Val(777)))); - assert_eq!( - entity("val(3.14)"), - Ok(("(3.14)", PropName("val".to_owned()))) - ); - assert_eq!(entity("val(-0)"), Ok(("(-0)", PropName("val".to_owned())))); - assert_eq!(entity("prop(name)"), Ok(("", PropName("name".to_owned())))); - assert_eq!(entity("prop"), Ok(("", PropName("prop".to_owned())))); - assert_eq!(entity("prop()"), Ok(("()", PropName("prop".to_owned())))); - assert_eq!(entity("some()"), Ok(("()", PropName("some".to_owned())))); - - assert!(entity("0xEF").is_err()); - } - - #[test] - fn test_operator() { - use super::Operator::{ - Contains, EndsWith, Equal, GreaterThan, GreaterThanOrEqualTo, LessThan, - LessThanOrEqualTo, NotEqual, StartsWith, - }; - - assert_eq!(operator("!="), Ok(("", NotEqual))); - assert_eq!(operator("$="), Ok(("", EndsWith))); - assert_eq!(operator("*="), Ok(("", Contains))); - assert_eq!(operator("<="), Ok(("", LessThanOrEqualTo))); - assert_eq!(operator(">="), Ok(("", GreaterThanOrEqualTo))); - assert_eq!(operator("^="), Ok(("", StartsWith))); - assert_eq!(operator("<"), Ok(("", LessThan))); - assert_eq!(operator("="), Ok(("", Equal))); - assert_eq!(operator(">"), Ok(("", GreaterThan))); - - assert!(operator("?").is_err()); - assert!(operator("?=").is_err()); - assert!(operator("?= ...").is_err()); - } -}