use std::collections::HashMap;
use std::fmt;
use std::ops::Range;
use serde_json::Value;
use crate::{parser::iter::BranchIter, trim::TrimHint};
static WHITESPACE: &str = "~";
static ROOT: &str = "@root";
pub trait Slice<'source>: fmt::Display + fmt::Debug {
fn as_str(&self) -> &'source str;
fn source(&self) -> &'source str;
}
pub trait Element<'source> {
fn open(&self) -> &'source str;
fn close(&self) -> &'source str;
fn open_span(&self) -> &Range<usize>;
fn close_span(&self) -> &Option<Range<usize>>;
fn is_closed(&self) -> bool;
fn exit(&mut self, close: Range<usize>);
fn span(&self) -> Range<usize> {
if let Some(ref close) = self.close_span() {
self.open_span().start..close.end
} else {
self.open_span().clone()
}
}
}
#[derive(Eq, PartialEq)]
pub enum Node<'source> {
Document(Document<'source>),
Text(Text<'source>),
Statement(Call<'source>),
Block(Block<'source>),
RawStatement(TextBlock<'source>),
RawComment(TextBlock<'source>),
Comment(TextBlock<'source>),
}
impl<'source> Node<'source> {
pub fn trim(&self) -> TrimHint {
TrimHint {
before: self.trim_before(),
after: self.trim_after(),
}
}
fn trim_before(&self) -> bool {
match *self {
Self::Document(_)
| Self::Text(_)
| Self::RawStatement(_)
| Self::RawComment(_)
| Self::Comment(_) => false,
Self::Statement(ref n) => n.trim_before(),
Self::Block(ref n) => n.trim_before(),
}
}
fn trim_after(&self) -> bool {
match *self {
Self::Document(_)
| Self::Text(_)
| Self::RawStatement(_)
| Self::RawComment(_)
| Self::Comment(_) => false,
Self::Statement(ref n) => n.trim_after(),
Self::Block(ref n) => n.trim_after(),
}
}
pub fn into_iter<'a>(&'a self) -> BranchIter<'a> {
BranchIter::new(self)
}
}
impl<'source> Slice<'source> for Node<'source> {
fn as_str(&self) -> &'source str {
match *self {
Self::Document(ref n) => n.as_str(),
Self::Text(ref n) => n.as_str(),
Self::Statement(ref n) => n.as_str(),
Self::Block(ref n) => n.as_str(),
Self::RawStatement(ref n)
| Self::RawComment(ref n)
| Self::Comment(ref n) => n.as_str(),
}
}
fn source(&self) -> &'source str {
match *self {
Self::Document(ref n) => n.source(),
Self::Text(ref n) => n.source(),
Self::RawStatement(ref n) => n.source(),
Self::RawComment(ref n) => n.source(),
Self::Comment(ref n) => n.source(),
Self::Statement(ref n) => n.source(),
Self::Block(ref n) => n.source(),
}
}
}
impl fmt::Display for Node<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::Document(ref n) => n.fmt(f),
Self::Text(ref n) => n.fmt(f),
Self::Statement(ref n) => n.fmt(f),
Self::Block(ref n) => n.fmt(f),
Self::RawStatement(ref n)
| Self::RawComment(ref n)
| Self::Comment(ref n) => n.fmt(f),
}
}
}
impl fmt::Debug for Node<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::Document(ref n) => fmt::Debug::fmt(n, f),
Self::Text(ref n) => fmt::Debug::fmt(n, f),
Self::Block(ref n) => fmt::Debug::fmt(n, f),
Self::Statement(ref n) => fmt::Debug::fmt(n, f),
Self::RawStatement(ref n)
| Self::RawComment(ref n)
| Self::Comment(ref n) => fmt::Debug::fmt(n, f),
}
}
}
#[derive(Eq, PartialEq)]
pub struct Text<'source>(pub &'source str, pub Range<usize>);
impl<'source> Slice<'source> for Text<'source> {
fn as_str(&self) -> &'source str {
&self.0[self.1.start..self.1.end]
}
fn source(&self) -> &'source str {
self.0
}
}
impl fmt::Display for Text<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl fmt::Debug for Text<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Text")
.field("source", &self.as_str())
.field("range", &self.1)
.finish()
}
}
#[derive(Eq, PartialEq)]
pub struct TextBlock<'source> {
source: &'source str,
text: Text<'source>,
open: Range<usize>,
close: Range<usize>,
}
impl<'source> TextBlock<'source> {
pub fn new(
source: &'source str,
text: Text<'source>,
open: Range<usize>,
close: Range<usize>,
) -> Self {
Self {
source,
text,
open,
close,
}
}
}
impl<'source> Slice<'source> for TextBlock<'source> {
fn as_str(&self) -> &'source str {
&self.source[self.open.start..self.close.end]
}
fn source(&self) -> &'source str {
self.source
}
}
impl<'source> Into<Text<'source>> for TextBlock<'source> {
fn into(self) -> Text<'source> {
self.text
}
}
impl fmt::Display for TextBlock<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl fmt::Debug for TextBlock<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TextBlock")
.field("source", &self.as_str())
.field("open", &self.open)
.field("close", &self.close)
.finish()
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum RawIdType {
Single,
Double,
Array,
}
#[derive(Debug, Eq, PartialEq)]
pub enum ComponentType {
Parent,
ThisKeyword,
ThisDotSlash,
Identifier,
LocalIdentifier,
RawIdentifier(RawIdType),
Delimiter,
}
#[derive(Eq, PartialEq)]
pub struct Component<'source> {
source: &'source str,
kind: ComponentType,
span: Range<usize>,
value: Option<String>,
}
impl<'source> Component<'source> {
pub fn new(
source: &'source str,
kind: ComponentType,
span: Range<usize>,
value: Option<String>,
) -> Self {
Self {
source,
kind,
span,
value,
}
}
pub fn is_root(&self) -> bool {
self.as_str() == ROOT
}
pub fn kind(&self) -> &ComponentType {
&self.kind
}
pub fn span(&self) -> &Range<usize> {
&self.span
}
pub fn is_local(&self) -> bool {
&ComponentType::LocalIdentifier == self.kind()
}
pub fn is_identifier(&self) -> bool {
&ComponentType::Identifier == self.kind()
}
pub fn is_explicit(&self) -> bool {
&ComponentType::ThisKeyword == self.kind()
|| self.is_explicit_dot_slash()
}
pub fn is_explicit_dot_slash(&self) -> bool {
&ComponentType::ThisDotSlash == self.kind()
}
pub fn as_value(&self) -> &str {
if let Some(ref value) = self.value {
return value;
}
self.as_str()
}
}
impl<'source> Slice<'source> for Component<'source> {
fn as_str(&self) -> &'source str {
&self.source[self.span().start..self.span().end]
}
fn source(&self) -> &'source str {
self.source
}
}
impl fmt::Display for Component<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl fmt::Debug for Component<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Component")
.field("source", &self.as_str())
.field("kind", &self.kind)
.field("span", &self.span)
.finish()
}
}
#[derive(Eq, PartialEq)]
pub struct Path<'source> {
source: &'source str,
components: Vec<Component<'source>>,
parents: u8,
explicit: bool,
root: bool,
}
impl<'source> Path<'source> {
pub fn new(source: &'source str) -> Self {
Self {
source,
components: Vec::new(),
parents: 0,
explicit: false,
root: false,
}
}
pub fn add_component(&mut self, part: Component<'source>) {
self.components.push(part);
}
pub fn components(&self) -> &Vec<Component<'source>> {
&self.components
}
pub fn parents(&self) -> u8 {
self.parents
}
pub fn set_parents(&mut self, parents: u8) {
self.parents = parents;
}
pub fn is_root(&self) -> bool {
self.root
}
pub fn set_root(&mut self, root: bool) {
self.root = root;
}
pub fn is_explicit(&self) -> bool {
self.explicit
}
pub fn set_explicit(&mut self, explicit: bool) {
self.explicit = explicit;
}
pub fn is_empty(&self) -> bool {
self.components.is_empty()
}
pub fn is_local(&self) -> bool {
return !self.components.is_empty()
&& self.components.first().unwrap().is_local();
}
pub fn is_simple(&self) -> bool {
return self.components.len() == 1
&& self.components.first().unwrap().kind
== ComponentType::Identifier;
}
}
impl<'source> Slice<'source> for Path<'source> {
fn as_str(&self) -> &'source str {
if !self.components.is_empty() {
let first = self.components.first().unwrap();
let last = self.components.last().unwrap();
&self.source[first.span().start..last.span().end]
} else {
""
}
}
fn source(&self) -> &'source str {
self.source
}
}
impl fmt::Display for Path<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl fmt::Debug for Path<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Path")
.field("source", &self.as_str())
.field("components", &self.components)
.field("parents", &self.parents)
.field("explicit", &self.explicit)
.field("root", &self.root)
.finish()
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum ParameterValue<'source> {
Path(Path<'source>),
Json(Value),
SubExpr(Call<'source>),
}
#[derive(Debug, Eq, PartialEq)]
pub enum CallTarget<'source> {
Path(Path<'source>),
SubExpr(Box<Call<'source>>),
}
impl<'source> CallTarget<'source> {
pub fn is_empty(&self) -> bool {
match *self {
Self::Path(ref path) => path.is_empty(),
Self::SubExpr(ref call) => call.is_empty(),
}
}
}
impl<'source> Slice<'source> for CallTarget<'source> {
fn as_str(&self) -> &'source str {
match *self {
Self::Path(ref path) => path.as_str(),
Self::SubExpr(ref call) => call.as_str(),
}
}
fn source(&self) -> &'source str {
match *self {
Self::Path(ref path) => path.source(),
Self::SubExpr(ref call) => call.source(),
}
}
}
impl fmt::Display for CallTarget<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl Default for CallTarget<'_> {
fn default() -> Self {
CallTarget::Path(Path::new(""))
}
}
#[derive(Default, Eq, PartialEq)]
pub struct Call<'source> {
source: &'source str,
partial: bool,
conditional: bool,
open: Range<usize>,
close: Option<Range<usize>>,
target: CallTarget<'source>,
arguments: Vec<ParameterValue<'source>>,
hash: HashMap<&'source str, ParameterValue<'source>>,
}
impl<'source> Call<'source> {
pub fn new(source: &'source str, open: Range<usize>) -> Self {
Self {
source,
partial: false,
conditional: false,
open,
close: None,
target: CallTarget::Path(Path::new(source)),
arguments: Vec::new(),
hash: HashMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.target.is_empty()
}
pub fn target(&self) -> &CallTarget<'source> {
&self.target
}
pub fn has_target(&self) -> bool {
self.target.as_str() != ""
}
pub fn set_target(&mut self, target: CallTarget<'source>) {
self.target = target;
}
pub fn add_argument(&mut self, arg: ParameterValue<'source>) {
self.arguments.push(arg);
}
pub fn arguments(&self) -> &Vec<ParameterValue<'source>> {
&self.arguments
}
pub fn add_hash(
&mut self,
key: &'source str,
val: ParameterValue<'source>,
) {
self.hash.insert(key, val);
}
pub fn hash(&self) -> &HashMap<&'source str, ParameterValue<'source>> {
&self.hash
}
pub fn is_partial(&self) -> bool {
self.partial
}
pub fn set_partial(&mut self, partial: bool) {
self.partial = partial;
}
pub fn is_conditional(&self) -> bool {
self.conditional
}
pub fn set_conditional(&mut self, conditional: bool) {
self.conditional = conditional;
}
pub fn is_escaped(&self) -> bool {
!self.open().starts_with("{{{")
}
fn trim_before(&self) -> bool {
self.open().ends_with(WHITESPACE)
}
fn trim_after(&self) -> bool {
self.close().starts_with(WHITESPACE)
}
}
impl<'source> Slice<'source> for Call<'source> {
fn as_str(&self) -> &'source str {
if let Some(ref close) = self.close {
return &self.source[self.open.start..close.end];
}
&self.source[self.open.start..self.open.end]
}
fn source(&self) -> &'source str {
self.source
}
}
impl<'source> Element<'source> for Call<'source> {
fn open(&self) -> &'source str {
&self.source[self.open.start..self.open.end]
}
fn close(&self) -> &'source str {
if let Some(ref close) = self.close {
return &self.source[close.start..close.end];
}
""
}
fn open_span(&self) -> &Range<usize> {
&self.open
}
fn close_span(&self) -> &Option<Range<usize>> {
&self.close
}
fn is_closed(&self) -> bool {
self.close.is_some()
}
fn exit(&mut self, close: Range<usize>) {
self.close = Some(close);
}
}
impl fmt::Display for Call<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl fmt::Debug for Call<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Call")
.field("source", &self.as_str())
.field("partial", &self.partial)
.field("open", &self.open)
.field("close", &self.close)
.field("target", &self.target)
.field("arguments", &self.arguments)
.field("hash", &self.hash)
.finish()
}
}
#[derive(Eq, PartialEq)]
pub struct Document<'source>(pub &'source str, pub Vec<Node<'source>>);
impl<'source> Document<'source> {
pub fn nodes(&self) -> &Vec<Node<'source>> {
&self.1
}
pub fn nodes_mut(&mut self) -> &mut Vec<Node<'source>> {
&mut self.1
}
}
impl<'source> Slice<'source> for Document<'source> {
fn as_str(&self) -> &'source str {
self.0
}
fn source(&self) -> &'source str {
self.0
}
}
impl fmt::Display for Document<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl fmt::Debug for Document<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Document").field("nodes", &self.1).finish()
}
}
#[derive(Eq, PartialEq)]
pub struct Block<'source> {
source: &'source str,
nodes: Vec<Node<'source>>,
raw: bool,
open: Range<usize>,
close: Option<Range<usize>>,
call: Call<'source>,
conditionals: Vec<Node<'source>>,
}
impl<'source> Block<'source> {
pub fn new(source: &'source str, open: Range<usize>, raw: bool) -> Self {
Self {
source,
nodes: Vec::new(),
raw,
open,
close: None,
call: Default::default(),
conditionals: Vec::new(),
}
}
pub fn call(&self) -> &Call<'source> {
&self.call
}
pub fn set_call(&mut self, call: Call<'source>) {
self.call = call;
}
pub fn name(&self) -> Option<&'source str> {
match self.call.target() {
CallTarget::Path(ref path) => {
if path.is_simple() {
let id = path.components().first().unwrap();
Some(id.as_str())
} else {
None
}
}
CallTarget::SubExpr(_) => None,
}
}
pub fn is_raw(&self) -> bool {
self.raw
}
pub fn add_condition(&mut self, condition: Block<'source>) {
self.close_condition(condition.call.open.clone());
self.conditionals.push(Node::Block(condition));
}
pub fn conditions(&self) -> &Vec<Node<'source>> {
&self.conditionals
}
pub fn push(&mut self, node: Node<'source>) {
if !self.conditionals.is_empty() {
let mut last = self.conditionals.last_mut().unwrap();
match &mut last {
Node::Block(ref mut condition) => {
condition.nodes.push(node);
}
_ => {}
}
} else {
self.nodes.push(node);
}
}
pub fn nodes(&self) -> &'source Vec<Node> {
&self.nodes
}
pub fn trim_close(&self) -> TrimHint {
TrimHint {
before: self.trim_before_close(),
after: self.trim_after_close(),
}
}
fn close_condition(&mut self, span: Range<usize>) {
if !self.conditionals.is_empty() {
if span.start > 0 {
let close = span.start - 1..span.start;
let mut last = self.conditionals.last_mut().unwrap();
match &mut last {
Node::Block(ref mut condition) => {
condition.close = Some(close);
}
_ => {}
}
}
}
}
fn trim_before(&self) -> bool {
let open = self.open();
if self.is_raw() {
open.len() > 4 && WHITESPACE == &open[4..5]
} else {
open.len() > 2 && WHITESPACE == &open[2..3]
}
}
fn trim_after(&self) -> bool {
self.call.trim_after()
}
fn trim_before_close(&self) -> bool {
let close = self.close();
if self.is_raw() {
close.len() > 4 && WHITESPACE == &close[4..5]
} else {
close.len() > 2 && WHITESPACE == &close[2..3]
}
}
fn trim_after_close(&self) -> bool {
let close = self.close();
if self.is_raw() {
if close.len() > 5 {
let index = close.len() - 5;
close.len() > 4 && WHITESPACE == &close[index..index + 1]
} else {
false
}
} else {
if close.len() > 3 {
let index = close.len() - 3;
close.len() > 2 && WHITESPACE == &close[index..index + 1]
} else {
false
}
}
}
}
impl<'source> Slice<'source> for Block<'source> {
fn as_str(&self) -> &'source str {
let close = self.close.clone().unwrap_or(0..self.source.len());
&self.source[self.open.start..close.end]
}
fn source(&self) -> &'source str {
self.source
}
}
impl<'source> Element<'source> for Block<'source> {
fn open(&self) -> &'source str {
&self.source[self.open.start..self.open.end]
}
fn close(&self) -> &'source str {
if let Some(ref close) = self.close {
&self.source[close.start..close.end]
} else {
""
}
}
fn open_span(&self) -> &Range<usize> {
&self.open
}
fn close_span(&self) -> &Option<Range<usize>> {
&self.close
}
fn is_closed(&self) -> bool {
self.close.is_some()
}
fn exit(&mut self, span: Range<usize>) {
if !self.conditionals.is_empty() {
let mut last = self.conditionals.last_mut().unwrap();
match &mut last {
Node::Block(ref mut condition) => {
condition.close = Some(span.clone());
}
_ => {}
}
}
self.close = Some(span);
}
}
impl fmt::Display for Block<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for t in self.nodes() {
t.fmt(f)?;
}
Ok(())
}
}
impl fmt::Debug for Block<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Block")
.field("open", &self.open)
.field("close", &self.close)
.field("call", &self.call)
.field("nodes", &self.nodes)
.finish()
}
}