nml/src/elements/raw.rs

308 lines
7.4 KiB
Rust
Raw Normal View History

2024-07-24 13:20:29 +02:00
use crate::compiler::compiler::Compiler;
use crate::document::document::Document;
use crate::document::element::ElemKind;
use crate::document::element::Element;
use crate::lua::kernel::CTX;
use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule;
use crate::parser::source::Source;
use crate::parser::source::Token;
use crate::parser::util::Property;
use crate::parser::util::PropertyMapError;
use crate::parser::util::PropertyParser;
use crate::parser::util::{self};
use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Error::BadArgument;
use mlua::Function;
use mlua::Lua;
use regex::Captures;
use regex::Regex;
use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::Arc;
2024-07-19 11:52:12 +02:00
#[derive(Debug)]
struct Raw {
2024-07-23 14:04:57 +02:00
pub(self) location: Token,
pub(self) kind: ElemKind,
pub(self) content: String,
2024-07-19 11:52:12 +02:00
}
impl Raw {
2024-07-24 13:20:29 +02:00
fn new(location: Token, kind: ElemKind, content: String) -> Self {
Self {
location,
kind,
content,
}
}
2024-07-19 11:52:12 +02:00
}
impl Element for Raw {
2024-07-24 13:20:29 +02:00
fn location(&self) -> &Token { &self.location }
fn kind(&self) -> ElemKind { self.kind.clone() }
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
fn element_name(&self) -> &'static str { "Raw" }
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
fn to_string(&self) -> String { format!("{self:#?}") }
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
fn compile(&self, _compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
2024-07-19 11:52:12 +02:00
Ok(self.content.clone())
2024-07-24 13:20:29 +02:00
}
2024-07-19 11:52:12 +02:00
}
pub struct RawRule {
re: [Regex; 1],
properties: PropertyParser,
}
impl RawRule {
pub fn new() -> Self {
let mut props = HashMap::new();
2024-07-24 13:20:29 +02:00
props.insert(
"kind".to_string(),
2024-07-19 11:52:12 +02:00
Property::new(
true,
"Element display kind".to_string(),
2024-07-24 13:20:29 +02:00
Some("inline".to_string()),
),
);
2024-07-19 11:52:12 +02:00
Self {
2024-07-24 13:20:29 +02:00
re: [
Regex::new(r"\{\?(?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)(\?\}))?")
.unwrap(),
2024-07-19 11:52:12 +02:00
],
2024-07-24 13:20:29 +02:00
properties: PropertyParser::new(props),
}
2024-07-19 11:52:12 +02:00
}
}
2024-07-24 13:20:29 +02:00
impl RegexRule for RawRule {
fn name(&self) -> &'static str { "Raw" }
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
fn regexes(&self) -> &[regex::Regex] { &self.re }
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
fn on_regex_match(
&self,
_index: usize,
parser: &dyn Parser,
document: &dyn Document,
token: Token,
matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
2024-07-19 11:52:12 +02:00
let mut reports = vec![];
2024-07-24 13:20:29 +02:00
let raw_content = match matches.get(2) {
2024-07-19 11:52:12 +02:00
// Unterminated
None => {
reports.push(
Report::build(ReportKind::Error, token.source(), token.start())
2024-07-24 13:20:29 +02:00
.with_message("Unterminated Raw Code")
.with_label(
Label::new((token.source().clone(), token.range.clone()))
.with_message(format!(
"Missing terminating `{}` after first `{}`",
"?}".fg(parser.colors().info),
"{?".fg(parser.colors().info)
))
.with_color(parser.colors().error),
)
.finish(),
);
return reports;
2024-07-19 11:52:12 +02:00
}
Some(content) => {
2024-07-24 13:20:29 +02:00
let processed =
util::process_escaped('\\', "?}", content.as_str().trim_start().trim_end());
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
if processed.is_empty() {
2024-07-19 11:52:12 +02:00
reports.push(
Report::build(ReportKind::Warning, token.source(), content.start())
2024-07-24 13:20:29 +02:00
.with_message("Empty Raw Code")
.with_label(
Label::new((token.source().clone(), content.range()))
.with_message("Raw code is empty")
.with_color(parser.colors().warning),
)
.finish(),
);
2024-07-19 11:52:12 +02:00
}
processed
}
};
2024-07-24 13:20:29 +02:00
let properties = match matches.get(1) {
2024-07-19 11:52:12 +02:00
None => match self.properties.default() {
Ok(properties) => properties,
Err(e) => {
reports.push(
Report::build(ReportKind::Error, token.source(), token.start())
2024-07-24 13:20:29 +02:00
.with_message("Invalid Raw Code")
.with_label(
Label::new((token.source().clone(), token.range.clone()))
.with_message(format!("Raw code is missing properties: {e}"))
.with_color(parser.colors().error),
)
.finish(),
);
return reports;
}
},
2024-07-19 11:52:12 +02:00
Some(props) => {
2024-07-24 13:20:29 +02:00
let processed =
util::process_escaped('\\', "]", props.as_str().trim_start().trim_end());
match self.properties.parse(processed.as_str()) {
2024-07-19 11:52:12 +02:00
Err(e) => {
reports.push(
Report::build(ReportKind::Error, token.source(), props.start())
2024-07-24 13:20:29 +02:00
.with_message("Invalid Raw Code Properties")
.with_label(
Label::new((token.source().clone(), props.range()))
.with_message(e)
.with_color(parser.colors().error),
)
.finish(),
);
2024-07-19 11:52:12 +02:00
return reports;
}
2024-07-24 13:20:29 +02:00
Ok(properties) => properties,
2024-07-19 11:52:12 +02:00
}
}
};
2024-07-24 13:20:29 +02:00
let raw_kind: ElemKind = match properties.get("kind", |prop, value| {
ElemKind::from_str(value.as_str()).map_err(|e| (prop, e))
}) {
2024-07-19 11:52:12 +02:00
Ok((_prop, kind)) => kind,
2024-07-24 09:05:57 +02:00
Err(e) => match e {
PropertyMapError::ParseError((prop, err)) => {
reports.push(
Report::build(ReportKind::Error, token.source(), token.start())
2024-07-24 13:20:29 +02:00
.with_message("Invalid Raw Code Property")
.with_label(
Label::new((token.source().clone(), token.range.clone()))
.with_message(format!(
"Property `kind: {}` cannot be converted: {}",
prop.fg(parser.colors().info),
err.fg(parser.colors().error)
))
.with_color(parser.colors().warning),
)
.finish(),
);
return reports;
}
2024-07-24 09:05:57 +02:00
PropertyMapError::NotFoundError(err) => {
reports.push(
Report::build(ReportKind::Error, token.source(), token.start())
2024-07-24 13:20:29 +02:00
.with_message("Invalid Code Property")
.with_label(
Label::new((
token.source().clone(),
token.start() + 1..token.end(),
))
.with_message(format!(
"Property `{}` is missing",
err.fg(parser.colors().info)
))
.with_color(parser.colors().warning),
)
.finish(),
);
return reports;
2024-07-24 09:05:57 +02:00
}
2024-07-24 13:20:29 +02:00
},
2024-07-19 11:52:12 +02:00
};
2024-07-24 13:20:29 +02:00
parser.push(
document,
Box::new(Raw {
location: token.clone(),
kind: raw_kind,
content: raw_content,
}),
);
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
reports
}
2024-07-21 15:56:56 +02:00
2024-07-30 11:01:22 +02:00
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
2024-07-23 14:04:57 +02:00
let mut bindings = vec![];
2024-07-24 13:20:29 +02:00
bindings.push((
"push".to_string(),
lua.create_function(|_, (kind, content): (String, String)| {
// Validate kind
let kind = match ElemKind::from_str(kind.as_str()) {
Ok(kind) => kind,
Err(e) => {
return Err(BadArgument {
to: Some("push".to_string()),
pos: 1,
name: Some("kind".to_string()),
cause: Arc::new(mlua::Error::external(format!(
"Wrong section kind specified: {e}"
))),
})
}
};
CTX.with_borrow(|ctx| {
ctx.as_ref().map(|ctx| {
ctx.parser.push(
ctx.document,
Box::new(Raw {
location: ctx.location.clone(),
kind,
content,
}),
);
})
});
Ok(())
})
.unwrap(),
));
2024-07-30 11:01:22 +02:00
Some(bindings)
2024-07-24 13:20:29 +02:00
}
2024-07-19 11:52:12 +02:00
}
2024-07-26 18:18:28 +02:00
#[cfg(test)]
mod tests {
use super::*;
use crate::compiler::compiler::Target;
use crate::parser::langparser::LangParser;
use crate::parser::source::SourceFile;
#[test]
fn raw_tests() {
let source = Rc::new(SourceFile::with_content(
"".to_string(),
r#"
Break{?[kind=block]<RAW>?}NewParagraph
"#
.to_string(),
None,
));
let parser = LangParser::default();
let compiler = Compiler::new(Target::HTML, None);
let doc = parser.parse(source, None);
let borrow = doc.content().borrow();
let found = borrow
.iter()
.filter_map(|e| e.downcast_ref::<Raw>())
.collect::<Vec<_>>();
assert_eq!(found[0].compile(&compiler, &*doc), Ok("<RAW>".to_string()));
//assert_eq!(found[1].compile(&compiler, &*doc), Ok("<RAW>".to_string()));
}
}