Add documentation
Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
ea75da491d
commit
4c8938e4ff
10 changed files with 160 additions and 9 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
|
@ -240,6 +240,15 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "document-features"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
|
||||||
|
dependencies = [
|
||||||
|
"litrs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
|
|
@ -420,6 +429,12 @@ version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litrs"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.29"
|
version = "0.4.29"
|
||||||
|
|
@ -439,7 +454,9 @@ dependencies = [
|
||||||
"annotate-snippets",
|
"annotate-snippets",
|
||||||
"criterion",
|
"criterion",
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
|
"document-features",
|
||||||
"insta",
|
"insta",
|
||||||
|
"nomo",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"test_each_file",
|
"test_each_file",
|
||||||
|
|
|
||||||
13
Cargo.toml
13
Cargo.toml
|
|
@ -16,9 +16,14 @@ harness = false
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
[lints.rust]
|
||||||
|
unsafe_code = "forbid"
|
||||||
|
missing_docs = "warn"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
annotate-snippets = "0.12.13"
|
annotate-snippets = "0.12.13"
|
||||||
displaydoc = "0.2.5"
|
displaydoc = "0.2.5"
|
||||||
|
document-features = { version = "0.2.12", optional = true }
|
||||||
serde_json = { version = "1.0.149", optional = true }
|
serde_json = { version = "1.0.149", optional = true }
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
winnow = { version = "0.7.14", features = ["unstable-recover"] }
|
winnow = { version = "0.7.14", features = ["unstable-recover"] }
|
||||||
|
|
@ -30,11 +35,19 @@ insta = { version = "1.46.3", features = ["glob", "serde"] }
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
test_each_file = "0.3.7"
|
test_each_file = "0.3.7"
|
||||||
|
nomo = { path = ".", features = ["unstable-pub"] }
|
||||||
|
|
||||||
[profile.dev.package]
|
[profile.dev.package]
|
||||||
insta.opt-level = 3
|
insta.opt-level = 3
|
||||||
similar.opt-level = 3
|
similar.opt-level = 3
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["document-features"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["serde_json"]
|
default = ["serde_json"]
|
||||||
|
## Add support for inserting [`serde_json::Value`]s into [`Context`] objects
|
||||||
serde_json = ["dep:serde_json"]
|
serde_json = ["dep:serde_json"]
|
||||||
|
## Get access to the internals of the crate ⚠️ This is a perma-unstable feature ⚠️
|
||||||
|
unstable-pub = []
|
||||||
|
document-features = ["dep:document-features"]
|
||||||
|
|
|
||||||
2
fuzz/Cargo.lock
generated
2
fuzz/Cargo.lock
generated
|
|
@ -112,7 +112,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nomo"
|
name = "nomo"
|
||||||
version = "0.1.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"annotate-snippets",
|
"annotate-snippets",
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ libfuzzer-sys = "0.4"
|
||||||
|
|
||||||
[dependencies.nomo]
|
[dependencies.nomo]
|
||||||
path = ".."
|
path = ".."
|
||||||
|
features = ["unstable-pub"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "fuzz_target_1"
|
name = "fuzz_target_1"
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ pub enum Instruction {
|
||||||
slot: VariableSlot,
|
slot: VariableSlot,
|
||||||
},
|
},
|
||||||
PushScope {
|
PushScope {
|
||||||
|
#[expect(unused)]
|
||||||
inherit_parent: bool,
|
inherit_parent: bool,
|
||||||
},
|
},
|
||||||
Abort,
|
Abort,
|
||||||
|
|
@ -93,6 +94,7 @@ pub enum Instruction {
|
||||||
value_slot: VariableSlot,
|
value_slot: VariableSlot,
|
||||||
},
|
},
|
||||||
LoadLiteralToSlot {
|
LoadLiteralToSlot {
|
||||||
|
#[allow(unused)]
|
||||||
source: TemplateToken,
|
source: TemplateToken,
|
||||||
value: NomoValue,
|
value: NomoValue,
|
||||||
slot: VariableSlot,
|
slot: VariableSlot,
|
||||||
|
|
|
||||||
|
|
@ -7,42 +7,55 @@ use thiserror::Error;
|
||||||
use crate::NomoValueError;
|
use crate::NomoValueError;
|
||||||
use crate::value::NomoValue;
|
use crate::value::NomoValue;
|
||||||
|
|
||||||
|
/// Possible errors while executing a function
|
||||||
#[derive(Debug, Error, Display)]
|
#[derive(Debug, Error, Display)]
|
||||||
pub enum NomoFunctionError {
|
pub enum NomoFunctionError {
|
||||||
/// Received {received} arguments, but this function only takes {expected}
|
/// Received {received} arguments, but this function only takes {expected}
|
||||||
|
#[expect(missing_docs)]
|
||||||
WrongArgumentCount { received: usize, expected: usize },
|
WrongArgumentCount { received: usize, expected: usize },
|
||||||
|
|
||||||
/// The argument at this position is of the wrong type
|
/// The argument at this position is of the wrong type
|
||||||
|
#[expect(missing_docs)]
|
||||||
InvalidArgumentType { index: usize },
|
InvalidArgumentType { index: usize },
|
||||||
|
|
||||||
/// A user-provided error
|
/// A user-provided error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
CustomError {
|
CustomError {
|
||||||
|
#[expect(missing_docs)]
|
||||||
custom: Box<dyn std::error::Error + Send + Sync>,
|
custom: Box<dyn std::error::Error + Send + Sync>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A function that can be used inside a template
|
||||||
pub trait NomoFunction<T>: 'static + Send + Sync {
|
pub trait NomoFunction<T>: 'static + Send + Sync {
|
||||||
|
/// Call the function with the given arguments
|
||||||
fn call(&self, args: Vec<NomoValue>) -> Result<NomoValue, NomoFunctionError>;
|
fn call(&self, args: Vec<NomoValue>) -> Result<NomoValue, NomoFunctionError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-pub")]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FunctionMap {
|
pub struct FunctionMap {
|
||||||
funcs: HashMap<String, ErasedNomoFunction>,
|
funcs: HashMap<String, ErasedNomoFunction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unstable-pub"))]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct FunctionMap {
|
||||||
|
funcs: HashMap<String, ErasedNomoFunction>,
|
||||||
|
}
|
||||||
|
|
||||||
impl FunctionMap {
|
impl FunctionMap {
|
||||||
pub fn register<NF: NomoFunction<T>, T>(&mut self, name: impl Into<String>, func: NF) {
|
pub fn register<NF: NomoFunction<T>, T>(&mut self, name: impl Into<String>, func: NF) {
|
||||||
self.funcs
|
self.funcs
|
||||||
.insert(name.into(), ErasedNomoFunction::erase(func));
|
.insert(name.into(), ErasedNomoFunction::erase(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, name: impl AsRef<str>) -> Option<&ErasedNomoFunction> {
|
pub(crate) fn get(&self, name: impl AsRef<str>) -> Option<&ErasedNomoFunction> {
|
||||||
self.funcs.get(name.as_ref())
|
self.funcs.get(name.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ErasedNomoFunction {
|
pub(crate) struct ErasedNomoFunction {
|
||||||
func: Box<dyn Any + Send + Sync>,
|
func: Box<dyn Any + Send + Sync>,
|
||||||
call_fn: fn(&dyn Any, Vec<NomoValue>) -> Result<NomoValue, NomoFunctionError>,
|
call_fn: fn(&dyn Any, Vec<NomoValue>) -> Result<NomoValue, NomoFunctionError>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
src/input.rs
11
src/input.rs
|
|
@ -8,6 +8,7 @@ use winnow::stream::Offset;
|
||||||
use winnow::stream::Stream;
|
use winnow::stream::Stream;
|
||||||
use winnow::stream::StreamIsPartial;
|
use winnow::stream::StreamIsPartial;
|
||||||
|
|
||||||
|
/// The input for templates in [nomo](crate)
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct NomoInput {
|
pub struct NomoInput {
|
||||||
backing: Arc<str>,
|
backing: Arc<str>,
|
||||||
|
|
@ -15,19 +16,26 @@ pub struct NomoInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NomoInput {
|
impl NomoInput {
|
||||||
|
/// Manually create an input
|
||||||
|
///
|
||||||
|
/// While it is not unsafe to pass in mismatched informations, the outcome will most likely not
|
||||||
|
/// work and possibly even panic later
|
||||||
pub fn from_parts(backing: Arc<str>, range: Range<usize>) -> NomoInput {
|
pub fn from_parts(backing: Arc<str>, range: Range<usize>) -> NomoInput {
|
||||||
NomoInput { backing, range }
|
NomoInput { backing, range }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turn the input into its parts
|
||||||
pub fn into_parts(self) -> (Arc<str>, Range<usize>) {
|
pub fn into_parts(self) -> (Arc<str>, Range<usize>) {
|
||||||
(self.backing, self.range)
|
(self.backing, self.range)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the range this input covers
|
||||||
pub fn get_range(&self) -> Range<usize> {
|
pub fn get_range(&self) -> Range<usize> {
|
||||||
self.range.clone()
|
self.range.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NomoInputCheckpoint {
|
pub struct NomoInputCheckpoint {
|
||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
|
|
@ -78,6 +86,7 @@ impl Deref for NomoInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NomoInput {
|
impl NomoInput {
|
||||||
|
/// Get the input as a [`str`]
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
self.deref()
|
self.deref()
|
||||||
}
|
}
|
||||||
|
|
@ -101,6 +110,8 @@ impl Offset for NomoInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub struct NomoInputIter {
|
pub struct NomoInputIter {
|
||||||
idx: usize,
|
idx: usize,
|
||||||
input: NomoInput,
|
input: NomoInput,
|
||||||
|
|
|
||||||
92
src/lib.rs
92
src/lib.rs
|
|
@ -1,3 +1,33 @@
|
||||||
|
//! # Nomo, the templating library
|
||||||
|
//!
|
||||||
|
//! To get started, add the crate to your project:
|
||||||
|
//!
|
||||||
|
//! ```bash
|
||||||
|
//! cargo add nomo
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Then, load some templates:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
//! let mut templates = nomo::Nomo::new();
|
||||||
|
//! templates.add_template("index.html", "<!DOCTYPE html><h1>Hello {{= name }}!</h1>");
|
||||||
|
//!
|
||||||
|
//! let mut context = nomo::Context::new();
|
||||||
|
//! context.insert("name", "World");
|
||||||
|
//! let result = templates.render("index.html", &context)?;
|
||||||
|
//!
|
||||||
|
//! assert_eq!(result, "<!DOCTYPE html><h1>Hello World!</h1>");
|
||||||
|
//! # Ok(()) }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The crate has the following feature flags:
|
||||||
|
#![cfg_attr(
|
||||||
|
feature = "document-features",
|
||||||
|
cfg_attr(doc, doc = ::document_features::document_features!())
|
||||||
|
)]
|
||||||
|
//!
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use displaydoc::Display;
|
use displaydoc::Display;
|
||||||
|
|
@ -5,34 +35,65 @@ use thiserror::Error;
|
||||||
|
|
||||||
use crate::compiler::VMInstructions;
|
use crate::compiler::VMInstructions;
|
||||||
use crate::functions::FunctionMap;
|
use crate::functions::FunctionMap;
|
||||||
|
use crate::functions::NomoFunction;
|
||||||
use crate::input::NomoInput;
|
use crate::input::NomoInput;
|
||||||
use crate::value::NomoValue;
|
use crate::value::NomoValue;
|
||||||
use crate::value::NomoValueError;
|
use crate::value::NomoValueError;
|
||||||
|
|
||||||
pub mod compiler;
|
macro_rules! unstable_pub {
|
||||||
pub mod eval;
|
($(#[$m:meta])* mod $name:ident) => {
|
||||||
|
$(#[$m])*
|
||||||
|
#[cfg(feature = "unstable-pub")]
|
||||||
|
pub mod $name;
|
||||||
|
#[cfg(not(feature = "unstable-pub"))]
|
||||||
|
mod $name;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unstable_pub!(
|
||||||
|
/// The compiler internals
|
||||||
|
mod compiler
|
||||||
|
);
|
||||||
|
unstable_pub!(
|
||||||
|
/// Evaluation internals
|
||||||
|
mod eval
|
||||||
|
);
|
||||||
|
unstable_pub!(
|
||||||
|
/// Lexer internals
|
||||||
|
mod lexer
|
||||||
|
);
|
||||||
|
unstable_pub!(
|
||||||
|
/// Parser internals
|
||||||
|
mod parser
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Nomo Functions
|
||||||
pub mod functions;
|
pub mod functions;
|
||||||
|
/// Input for nomo
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod lexer;
|
/// Values used in Nomo
|
||||||
pub mod parser;
|
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
|
/// Errors related to parsing and evaluating templates
|
||||||
#[derive(Debug, Error, Display)]
|
#[derive(Debug, Error, Display)]
|
||||||
pub enum NomoError {
|
pub enum NomoError {
|
||||||
/// Could not parse the given template
|
/// Could not parse the given template
|
||||||
ParseError {
|
ParseError {
|
||||||
#[from]
|
#[from]
|
||||||
|
#[expect(missing_docs)]
|
||||||
source: lexer::ParseFailure,
|
source: lexer::ParseFailure,
|
||||||
},
|
},
|
||||||
/// Invalid Template
|
/// Invalid Template
|
||||||
AstError {
|
AstError {
|
||||||
#[from]
|
#[from]
|
||||||
|
#[expect(missing_docs)]
|
||||||
source: parser::AstFailure,
|
source: parser::AstFailure,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// An error occurred while evaluating
|
/// An error occurred while evaluating
|
||||||
EvaluationError {
|
EvaluationError {
|
||||||
#[from]
|
#[from]
|
||||||
|
#[expect(missing_docs)]
|
||||||
source: eval::EvaluationError,
|
source: eval::EvaluationError,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -40,6 +101,7 @@ pub enum NomoError {
|
||||||
UnknownTemplate(String),
|
UnknownTemplate(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The main struct and entry point for the [`nomo`](crate)
|
||||||
pub struct Nomo {
|
pub struct Nomo {
|
||||||
templates: HashMap<String, Template>,
|
templates: HashMap<String, Template>,
|
||||||
function_map: FunctionMap,
|
function_map: FunctionMap,
|
||||||
|
|
@ -52,6 +114,7 @@ impl Default for Nomo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Nomo {
|
impl Nomo {
|
||||||
|
/// Create a new Nomo Instance
|
||||||
pub fn new() -> Nomo {
|
pub fn new() -> Nomo {
|
||||||
Nomo {
|
Nomo {
|
||||||
templates: HashMap::new(),
|
templates: HashMap::new(),
|
||||||
|
|
@ -59,6 +122,7 @@ impl Nomo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a new template
|
||||||
pub fn add_template(
|
pub fn add_template(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
|
|
@ -76,6 +140,17 @@ impl Nomo {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register a function to make it available when rendering
|
||||||
|
pub fn register_function<A>(&mut self, name: impl Into<String>, f: impl NomoFunction<A>) {
|
||||||
|
self.function_map.register(name, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List of currently available templates
|
||||||
|
pub fn templates(&self) -> Vec<String> {
|
||||||
|
self.templates.keys().cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a specific template
|
||||||
pub fn render(&self, name: &str, ctx: &Context) -> Result<String, NomoError> {
|
pub fn render(&self, name: &str, ctx: &Context) -> Result<String, NomoError> {
|
||||||
let template = self
|
let template = self
|
||||||
.templates
|
.templates
|
||||||
|
|
@ -92,6 +167,7 @@ struct Template {
|
||||||
instructions: VMInstructions,
|
instructions: VMInstructions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The context for a given render call
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
values: HashMap<String, NomoValue>,
|
values: HashMap<String, NomoValue>,
|
||||||
}
|
}
|
||||||
|
|
@ -103,12 +179,16 @@ impl Default for Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
|
/// Create new Context
|
||||||
pub fn new() -> Context {
|
pub fn new() -> Context {
|
||||||
Context {
|
Context {
|
||||||
values: HashMap::new(),
|
values: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a value into the map
|
||||||
|
///
|
||||||
|
/// If you enable the `serde_json` feature, you can insert [`serde_json::Value`]s
|
||||||
pub fn try_insert(
|
pub fn try_insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: impl Into<String>,
|
key: impl Into<String>,
|
||||||
|
|
@ -119,17 +199,19 @@ impl Context {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a value into the map, panic if you can't
|
||||||
pub fn insert(&mut self, key: impl Into<String>, value: impl Into<NomoValue>) {
|
pub fn insert(&mut self, key: impl Into<String>, value: impl Into<NomoValue>) {
|
||||||
self.values.insert(key.into(), value.into());
|
self.values.insert(key.into(), value.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access the values inside this context
|
||||||
pub fn values(&self) -> &HashMap<String, NomoValue> {
|
pub fn values(&self) -> &HashMap<String, NomoValue> {
|
||||||
&self.values
|
&self.values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SourceSpan {
|
struct SourceSpan {
|
||||||
pub range: std::ops::Range<usize>,
|
pub range: std::ops::Range<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,7 @@ pub enum TemplateAstExpr<'input> {
|
||||||
ElseConditional {
|
ElseConditional {
|
||||||
expression: Option<Box<TemplateAstExpr<'input>>>,
|
expression: Option<Box<TemplateAstExpr<'input>>>,
|
||||||
},
|
},
|
||||||
|
#[expect(unused)]
|
||||||
Invalid(&'input [TemplateToken]),
|
Invalid(&'input [TemplateToken]),
|
||||||
MathOperation {
|
MathOperation {
|
||||||
op: TokenOperator,
|
op: TokenOperator,
|
||||||
|
|
|
||||||
13
src/value.rs
13
src/value.rs
|
|
@ -1,11 +1,12 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
#[cfg(feature = "serde_json")]
|
|
||||||
use displaydoc::Display;
|
use displaydoc::Display;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Values to be used inside templates
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
#[expect(missing_docs)]
|
||||||
pub enum NomoValue {
|
pub enum NomoValue {
|
||||||
String {
|
String {
|
||||||
value: Cow<'static, str>,
|
value: Cow<'static, str>,
|
||||||
|
|
@ -35,6 +36,7 @@ pub enum NomoValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NomoValue {
|
impl NomoValue {
|
||||||
|
/// Return a str if there is one inside
|
||||||
pub fn as_str(&self) -> Option<&str> {
|
pub fn as_str(&self) -> Option<&str> {
|
||||||
if let Self::String { value } = self {
|
if let Self::String { value } = self {
|
||||||
Some(value)
|
Some(value)
|
||||||
|
|
@ -43,6 +45,7 @@ impl NomoValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return an arry if there is one inside
|
||||||
pub fn as_array(&self) -> Option<&[NomoValue]> {
|
pub fn as_array(&self) -> Option<&[NomoValue]> {
|
||||||
if let Self::Array { value } = self {
|
if let Self::Array { value } = self {
|
||||||
Some(value)
|
Some(value)
|
||||||
|
|
@ -51,6 +54,7 @@ impl NomoValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a bool if there is one inside
|
||||||
pub fn as_bool(&self) -> Option<bool> {
|
pub fn as_bool(&self) -> Option<bool> {
|
||||||
if let Self::Bool { value } = self {
|
if let Self::Bool { value } = self {
|
||||||
Some(*value)
|
Some(*value)
|
||||||
|
|
@ -61,6 +65,7 @@ impl NomoValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return an object if there is one inside
|
||||||
pub fn as_object(&self) -> Option<&BTreeMap<String, NomoValue>> {
|
pub fn as_object(&self) -> Option<&BTreeMap<String, NomoValue>> {
|
||||||
if let Self::Object { value } = self {
|
if let Self::Object { value } = self {
|
||||||
Some(value)
|
Some(value)
|
||||||
|
|
@ -69,6 +74,7 @@ impl NomoValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return an integer if there is one inside
|
||||||
pub fn as_integer(&self) -> Option<u64> {
|
pub fn as_integer(&self) -> Option<u64> {
|
||||||
if let Self::Integer { value } = self {
|
if let Self::Integer { value } = self {
|
||||||
Some(*value)
|
Some(*value)
|
||||||
|
|
@ -77,6 +83,7 @@ impl NomoValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a float if there is one inside
|
||||||
pub fn as_float(&self) -> Option<f64> {
|
pub fn as_float(&self) -> Option<f64> {
|
||||||
if let Self::Float { value } = self {
|
if let Self::Float { value } = self {
|
||||||
Some(*value)
|
Some(*value)
|
||||||
|
|
@ -85,6 +92,7 @@ impl NomoValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the iterator if there is one inside
|
||||||
pub fn as_iterator(&self) -> Option<&dyn CloneIterator<Item = NomoValue>> {
|
pub fn as_iterator(&self) -> Option<&dyn CloneIterator<Item = NomoValue>> {
|
||||||
if let Self::Iterator { value } = self {
|
if let Self::Iterator { value } = self {
|
||||||
Some(value)
|
Some(value)
|
||||||
|
|
@ -93,6 +101,7 @@ impl NomoValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the iterator mutably if there is one inside
|
||||||
pub fn as_iterator_mut(&mut self) -> Option<&mut dyn CloneIterator<Item = NomoValue>> {
|
pub fn as_iterator_mut(&mut self) -> Option<&mut dyn CloneIterator<Item = NomoValue>> {
|
||||||
if let Self::Iterator { value } = self {
|
if let Self::Iterator { value } = self {
|
||||||
Some(value)
|
Some(value)
|
||||||
|
|
@ -249,7 +258,9 @@ impl NomoValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marker trait for iterators that can be cloned
|
||||||
pub trait CloneIterator: Iterator<Item = NomoValue> {
|
pub trait CloneIterator: Iterator<Item = NomoValue> {
|
||||||
|
/// Create a new instance of the iterator inside a [Box]
|
||||||
fn clone_box(&self) -> Box<dyn CloneIterator<Item = NomoValue>>;
|
fn clone_box(&self) -> Box<dyn CloneIterator<Item = NomoValue>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue