use alloc::borrow::Cow;
use alloc::vec::Vec;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum Expression<'a> {
Literal(LiteralExpression<'a>),
Variable(VariableExpression<'a>),
Function(FunctionExpression<'a>),
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct LiteralExpression<'a> {
pub arg: Literal<'a>,
pub function: Option<FunctionRef<'a>>,
pub attributes: Attributes<'a>,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct VariableExpression<'a> {
pub arg: VariableRef<'a>,
pub function: Option<FunctionRef<'a>>,
pub attributes: Attributes<'a>,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct FunctionExpression<'a> {
pub function: FunctionRef<'a>,
pub attributes: Attributes<'a>,
}
impl<'a> Expression<'a> {
pub fn function(&self) -> Option<&FunctionRef<'a>> {
match self {
Expression::Literal(e) => e.function.as_ref(),
Expression::Variable(e) => e.function.as_ref(),
Expression::Function(e) => Some(&e.function),
}
}
pub fn attributes(&self) -> &Attributes<'a> {
match self {
Expression::Literal(e) => &e.attributes,
Expression::Variable(e) => &e.attributes,
Expression::Function(e) => &e.attributes,
}
}
pub fn into_owned(self) -> Expression<'static> {
match self {
Expression::Literal(e) => Expression::Literal(e.into_owned()),
Expression::Variable(e) => Expression::Variable(e.into_owned()),
Expression::Function(e) => Expression::Function(e.into_owned()),
}
}
}
impl LiteralExpression<'_> {
pub fn into_owned(self) -> LiteralExpression<'static> {
LiteralExpression {
arg: self.arg.into_owned(),
function: self.function.map(FunctionRef::into_owned),
attributes: self.attributes.into_owned(),
}
}
}
impl VariableExpression<'_> {
pub fn into_owned(self) -> VariableExpression<'static> {
VariableExpression {
arg: self.arg.into_owned(),
function: self.function.map(FunctionRef::into_owned),
attributes: self.attributes.into_owned(),
}
}
}
impl FunctionExpression<'_> {
pub fn into_owned(self) -> FunctionExpression<'static> {
FunctionExpression {
function: self.function.into_owned(),
attributes: self.attributes.into_owned(),
}
}
}
fn own(s: Cow<'_, str>) -> Cow<'static, str> {
Cow::Owned(s.into_owned())
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Literal<'a> {
pub value: Cow<'a, str>,
}
impl Literal<'_> {
pub fn into_owned(self) -> Literal<'static> {
Literal {
value: own(self.value),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct VariableRef<'a> {
pub name: Cow<'a, str>,
}
impl VariableRef<'_> {
pub fn into_owned(self) -> VariableRef<'static> {
VariableRef {
name: own(self.name),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct FunctionRef<'a> {
pub name: Cow<'a, str>,
pub options: Options<'a>,
}
impl FunctionRef<'_> {
pub fn into_owned(self) -> FunctionRef<'static> {
FunctionRef {
name: own(self.name),
options: self.options.into_owned(),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum OptionValue<'a> {
Literal(Literal<'a>),
Variable(VariableRef<'a>),
}
impl OptionValue<'_> {
pub fn into_owned(self) -> OptionValue<'static> {
match self {
OptionValue::Literal(l) => OptionValue::Literal(l.into_owned()),
OptionValue::Variable(v) => OptionValue::Variable(v.into_owned()),
}
}
}
#[derive(Clone, Default, PartialEq, Eq, Hash, Debug)]
pub struct Options<'a>(pub(crate) Vec<(Cow<'a, str>, OptionValue<'a>)>);
impl<'a> Options<'a> {
pub const fn new() -> Self {
Options(Vec::new())
}
pub fn with_capacity(capacity: usize) -> Self {
Options(Vec::with_capacity(capacity))
}
pub fn push(&mut self, name: Cow<'a, str>, value: OptionValue<'a>) {
self.0.push((name, value));
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &OptionValue<'a>)> {
self.0.iter().map(|(k, v)| (k.as_ref(), v))
}
pub fn get(&self, name: &str) -> Option<&OptionValue<'a>> {
self.0.iter().find(|(k, _)| k == name).map(|(_, v)| v)
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn into_owned(self) -> Options<'static> {
Options(
self.0
.into_iter()
.map(|(k, v)| (own(k), v.into_owned()))
.collect(),
)
}
}
impl<'a> FromIterator<(Cow<'a, str>, OptionValue<'a>)> for Options<'a> {
fn from_iter<I: IntoIterator<Item = (Cow<'a, str>, OptionValue<'a>)>>(iter: I) -> Self {
Options(iter.into_iter().collect())
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Markup<'a> {
pub kind: MarkupKind,
pub name: Cow<'a, str>,
pub options: Options<'a>,
pub attributes: Attributes<'a>,
}
impl Markup<'_> {
pub fn into_owned(self) -> Markup<'static> {
Markup {
kind: self.kind,
name: own(self.name),
options: self.options.into_owned(),
attributes: self.attributes.into_owned(),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum MarkupKind {
Open,
Standalone,
Close,
}
#[derive(Clone, Default, PartialEq, Eq, Hash, Debug)]
pub struct Attributes<'a>(pub(crate) Vec<(Cow<'a, str>, Option<Literal<'a>>)>);
impl<'a> Attributes<'a> {
pub const fn new() -> Self {
Attributes(Vec::new())
}
pub fn with_capacity(capacity: usize) -> Self {
Attributes(Vec::with_capacity(capacity))
}
pub fn push(&mut self, name: Cow<'a, str>, value: Option<Literal<'a>>) {
self.0.push((name, value));
}
pub fn iter(&self) -> impl Iterator<Item = (&str, Option<&Literal<'a>>)> {
self.0.iter().map(|(k, v)| (k.as_ref(), v.as_ref()))
}
pub fn get(&self, name: &str) -> Option<Option<&Literal<'a>>> {
self.0
.iter()
.rfind(|(k, _)| k == name)
.map(|(_, v)| v.as_ref())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn into_owned(self) -> Attributes<'static> {
Attributes(
self.0
.into_iter()
.map(|(k, v)| (own(k), v.map(Literal::into_owned)))
.collect(),
)
}
}
impl<'a> FromIterator<(Cow<'a, str>, Option<Literal<'a>>)> for Attributes<'a> {
fn from_iter<I: IntoIterator<Item = (Cow<'a, str>, Option<Literal<'a>>)>>(iter: I) -> Self {
Attributes(iter.into_iter().collect())
}
}
pub fn split_identifier(identifier: &str) -> (Option<&str>, &str) {
match identifier.split_once(':') {
Some((ns, name)) => (Some(ns), name),
None => (None, identifier),
}
}
#[cfg(test)]
mod tests {
use alloc::borrow::Cow;
use super::{
Attributes, Expression, FunctionExpression, FunctionRef, Literal, OptionValue, Options,
VariableExpression, VariableRef, split_identifier,
};
fn lit(s: &str) -> Literal<'_> {
Literal {
value: Cow::Borrowed(s),
}
}
#[test]
fn options_keep_order_and_duplicates() {
let mut o = Options::new();
assert!(o.is_empty());
o.push("b".into(), OptionValue::Literal(lit("1")));
o.push(
"a".into(),
OptionValue::Variable(VariableRef { name: "x".into() }),
);
o.push("b".into(), OptionValue::Literal(lit("2")));
assert_eq!(o.len(), 3);
let names: alloc::vec::Vec<&str> = o.iter().map(|(k, _)| k).collect();
assert_eq!(names, ["b", "a", "b"]);
assert_eq!(o.get("b"), Some(&OptionValue::Literal(lit("1"))));
assert_eq!(
o.get("a"),
Some(&OptionValue::Variable(VariableRef { name: "x".into() }))
);
assert_eq!(o.get("c"), None);
let mut n = Options::new();
n.push("\u{e9}".into(), OptionValue::Literal(lit("1")));
assert!(n.get("e\u{301}").is_none());
}
#[test]
fn attributes_keep_order_and_duplicates() {
let mut a = Attributes::new();
a.push("flag".into(), None);
a.push("x".into(), Some(lit("1")));
a.push("flag".into(), Some(lit("2")));
assert_eq!(a.len(), 3);
assert_eq!(a.get("flag"), Some(Some(&lit("2"))));
assert_eq!(a.get("x"), Some(Some(&lit("1"))));
assert_eq!(a.get("y"), None);
let all: alloc::vec::Vec<(&str, Option<&Literal<'_>>)> = a.iter().collect();
assert_eq!(all[2], ("flag", Some(&lit("2"))));
}
#[test]
fn split_identifiers() {
assert_eq!(split_identifier("ns:name"), (Some("ns"), "name"));
assert_eq!(split_identifier("name"), (None, "name"));
assert_eq!(split_identifier("u:dir"), (Some("u"), "dir"));
assert_eq!(split_identifier(""), (None, ""));
}
#[test]
fn expression_accessors_and_owning() {
let mut attrs = Attributes::new();
attrs.push("a".into(), None);
let f = FunctionRef {
name: "number".into(),
options: Options::new(),
};
let e = Expression::Variable(VariableExpression {
arg: VariableRef { name: "n".into() },
function: Some(f.clone()),
attributes: attrs.clone(),
});
assert_eq!(e.function(), Some(&f));
assert_eq!(e.attributes(), &attrs);
let fe = Expression::Function(FunctionExpression {
function: f.clone(),
attributes: Attributes::new(),
});
assert_eq!(fe.function().map(|f| f.name.as_ref()), Some("number"));
let owned: Expression<'static> = e.clone().into_owned();
assert_eq!(owned, e);
if let Expression::Variable(v) = owned {
assert!(matches!(v.arg.name, Cow::Owned(_)));
}
}
}