#![cfg_attr(not(feature = "spanned"), allow(dead_code))]
use core::num::NonZeroI64;
mod error;
mod eval;
mod parse;
mod span;
#[cfg(test)]
mod tests;
mod token;
pub use error::{FailReason, ParseFail};
pub use span::Span;
#[cfg(feature = "spanned")]
pub use span::Spanned;
type Input = char;
type Error = ParseFail<char, ()>;
pub struct Ident {
#[cfg(feature = "spanned")]
span: Span,
val: String,
}
impl Ident {
#[must_use]
pub fn as_str(&self) -> &str {
&self.val
}
}
pub struct BoolLit {
#[cfg(feature = "spanned")]
span: Span,
val: bool,
}
impl BoolLit {
#[must_use]
pub fn as_bool(&self) -> bool {
self.val
}
}
pub struct NullLit {
#[cfg(feature = "spanned")]
span: Span,
}
pub struct IntLit {
#[cfg(feature = "spanned")]
span: Span,
val: i64,
}
impl IntLit {
#[must_use]
pub fn as_int(&self) -> i64 {
self.val
}
}
pub struct NonZeroIntLit {
#[cfg(feature = "spanned")]
span: Span,
val: NonZeroI64,
}
impl NonZeroIntLit {
#[must_use]
pub fn as_int(&self) -> NonZeroI64 {
self.val
}
}
struct StringContent {
#[cfg(feature = "spanned")]
span: Span,
val: String,
}
pub struct SingleStringLit {
start: token::SingleQuote,
content: StringContent,
end: token::SingleQuote,
}
impl SingleStringLit {
#[must_use]
pub fn as_str(&self) -> &str {
&self.content.val
}
}
pub struct DoubleStringLit {
start: token::DoubleQuote,
content: StringContent,
end: token::DoubleQuote,
}
impl DoubleStringLit {
#[must_use]
pub fn as_str(&self) -> &str {
&self.content.val
}
}
pub enum StringLit {
Single(SingleStringLit),
Double(DoubleStringLit),
}
impl StringLit {
#[must_use]
pub fn as_str(&self) -> &str {
match self {
StringLit::Single(s) => s.as_str(),
StringLit::Double(s) => s.as_str(),
}
}
}
#[must_use = "A path does nothing on its own, call `find` or `find_str` to evaluate the path on a \
value"]
pub struct Path {
dollar: token::Dollar,
segments: Vec<Segment>,
tilde: Option<token::Tilde>,
}
impl Path {
#[must_use]
pub fn segments(&self) -> &[Segment] {
&self.segments
}
}
pub struct SubPath {
kind: PathKind,
segments: Vec<Segment>,
tilde: Option<token::Tilde>,
}
impl SubPath {
#[must_use]
pub fn kind(&self) -> &PathKind {
&self.kind
}
#[must_use]
pub fn segments(&self) -> &[Segment] {
&self.segments
}
#[must_use]
pub fn is_id(&self) -> bool {
self.tilde.is_some()
}
}
#[non_exhaustive]
pub enum PathKind {
Root(token::Dollar),
Relative(token::At),
}
impl PathKind {
#[must_use]
pub fn is_root(&self) -> bool {
matches!(self, PathKind::Root(_))
}
#[must_use]
pub fn is_relative(&self) -> bool {
matches!(self, PathKind::Relative(_))
}
}
#[non_exhaustive]
pub enum Segment {
Dot(token::Dot, RawSelector),
Bracket(token::Bracket, BracketSelector),
Recursive(token::DotDot, Option<RawSelector>),
}
#[non_exhaustive]
pub enum RawSelector {
Wildcard(token::Star),
Parent(token::Caret),
Name(Ident),
}
pub struct StepRange {
start: Option<IntLit>,
colon1: token::Colon,
end: Option<IntLit>,
colon2: token::Colon,
step: Option<NonZeroIntLit>,
}
impl StepRange {
#[must_use]
pub fn start_lit(&self) -> Option<&IntLit> {
self.start.as_ref()
}
#[must_use]
pub fn end_lit(&self) -> Option<&IntLit> {
self.end.as_ref()
}
#[must_use]
pub fn step_lit(&self) -> Option<&NonZeroIntLit> {
self.step.as_ref()
}
#[must_use]
pub fn start(&self) -> Option<i64> {
self.start.as_ref().map(|a| a.as_int())
}
#[must_use]
pub fn end(&self) -> Option<i64> {
self.end.as_ref().map(|a| a.as_int())
}
#[must_use]
pub fn step(&self) -> Option<NonZeroI64> {
self.step.as_ref().map(|a| a.as_int())
}
}
pub struct Range {
start: Option<IntLit>,
colon: token::Colon,
end: Option<IntLit>,
}
impl Range {
#[must_use]
pub fn start_lit(&self) -> Option<&IntLit> {
self.start.as_ref()
}
#[must_use]
pub fn end_lit(&self) -> Option<&IntLit> {
self.end.as_ref()
}
#[must_use]
pub fn start(&self) -> Option<i64> {
self.start.as_ref().map(|a| a.as_int())
}
#[must_use]
pub fn end(&self) -> Option<i64> {
self.end.as_ref().map(|a| a.as_int())
}
}
#[non_exhaustive]
pub enum UnionComponent {
StepRange(StepRange),
Range(Range),
Parent(token::Caret),
Path(SubPath),
Filter(Filter),
Literal(BracketLit),
}
impl TryFrom<BracketSelector> for UnionComponent {
type Error = ();
fn try_from(value: BracketSelector) -> Result<Self, Self::Error> {
Ok(match value {
BracketSelector::StepRange(sr) => UnionComponent::StepRange(sr),
BracketSelector::Range(r) => UnionComponent::Range(r),
BracketSelector::Parent(p) => UnionComponent::Parent(p),
BracketSelector::Path(p) => UnionComponent::Path(p),
BracketSelector::Filter(f) => UnionComponent::Filter(f),
BracketSelector::Literal(l) => UnionComponent::Literal(l),
_ => return Err(()),
})
}
}
#[non_exhaustive]
pub enum BracketSelector {
Union(Vec<UnionComponent>),
StepRange(StepRange),
Range(Range),
Wildcard(token::Star),
Parent(token::Caret),
Path(SubPath),
Filter(Filter),
Literal(BracketLit),
}
#[non_exhaustive]
pub enum BracketLit {
Int(IntLit),
String(StringLit),
}
impl BracketLit {
#[must_use]
pub fn is_int(&self) -> bool {
matches!(self, BracketLit::Int(_))
}
#[must_use]
pub fn is_str(&self) -> bool {
matches!(self, BracketLit::String(_))
}
#[must_use]
pub fn as_int(&self) -> Option<i64> {
if let BracketLit::Int(i) = self {
Some(i.as_int())
} else {
None
}
}
#[must_use]
pub fn as_str(&self) -> Option<&str> {
if let BracketLit::String(s) = self {
Some(s.as_str())
} else {
None
}
}
}
pub struct Filter {
question: token::Question,
paren: token::Paren,
inner: FilterExpr,
}
impl Filter {
#[must_use]
pub fn expression(&self) -> &FilterExpr {
&self.inner
}
}
#[non_exhaustive]
pub enum ExprLit {
Int(IntLit),
String(StringLit),
Bool(BoolLit),
Null(NullLit),
}
impl ExprLit {
#[must_use]
pub fn is_int(&self) -> bool {
matches!(self, ExprLit::Int(_))
}
#[must_use]
pub fn is_str(&self) -> bool {
matches!(self, ExprLit::String(_))
}
#[must_use]
pub fn is_bool(&self) -> bool {
matches!(self, ExprLit::Bool(_))
}
#[must_use]
pub fn is_null(&self) -> bool {
matches!(self, ExprLit::Null(_))
}
#[must_use]
pub fn as_int(&self) -> Option<i64> {
if let ExprLit::Int(i) = self {
Some(i.as_int())
} else {
None
}
}
#[must_use]
pub fn as_str(&self) -> Option<&str> {
if let ExprLit::String(s) = self {
Some(s.as_str())
} else {
None
}
}
#[must_use]
pub fn as_bool(&self) -> Option<bool> {
if let ExprLit::Bool(s) = self {
Some(s.as_bool())
} else {
None
}
}
}
#[non_exhaustive]
pub enum FilterExpr {
Unary(UnOp, Box<FilterExpr>),
Binary(Box<FilterExpr>, BinOp, Box<FilterExpr>),
Path(SubPath),
Lit(ExprLit),
Parens(token::Paren, Box<FilterExpr>),
}
#[non_exhaustive]
pub enum UnOp {
Neg(token::Dash),
Not(token::Bang),
}
#[non_exhaustive]
pub enum BinOp {
And(token::DoubleAnd),
Or(token::DoublePipe),
Eq(token::EqEq),
Le(token::LessEq),
Lt(token::LessThan),
Gt(token::GreaterThan),
Ge(token::GreaterEq),
Add(token::Plus),
Sub(token::Dash),
Mul(token::Star),
Div(token::RightSlash),
Rem(token::Percent),
}