nml/src/elements/link.rs

176 lines
4.4 KiB
Rust
Raw Normal View History

2024-07-24 13:20:29 +02:00
use crate::compiler::compiler::Compiler;
use crate::compiler::compiler::Target;
use crate::document::document::Document;
use crate::document::element::ElemKind;
use crate::document::element::Element;
use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule;
use crate::parser::source::Source;
use crate::parser::source::Token;
use crate::parser::util;
use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Captures;
use regex::Regex;
use std::ops::Range;
use std::rc::Rc;
2024-07-19 11:52:12 +02:00
#[derive(Debug)]
pub struct Link {
2024-07-24 13:20:29 +02:00
location: Token,
2024-07-19 11:52:12 +02:00
name: String, // Link name
2024-07-24 13:20:29 +02:00
url: String, // Link url
2024-07-19 11:52:12 +02:00
}
2024-07-24 13:20:29 +02:00
impl Link {
pub fn new(location: Token, name: String, url: String) -> Self {
Self {
location: location,
name,
url,
}
}
2024-07-19 11:52:12 +02:00
}
2024-07-24 13:20:29 +02:00
impl Element for Link {
fn location(&self) -> &Token { &self.location }
fn kind(&self) -> ElemKind { ElemKind::Inline }
fn element_name(&self) -> &'static str { "Link" }
fn to_string(&self) -> String { format!("{self:#?}") }
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
match compiler.target() {
Target::HTML => Ok(format!(
"<a href=\"{}\">{}</a>",
compiler.sanitize(self.url.as_str()),
compiler.sanitize(self.name.as_str()),
)),
Target::LATEX => Ok(format!(
"\\href{{{}}}{{{}}}",
compiler.sanitize(self.url.as_str()),
compiler.sanitize(self.name.as_str()),
)),
}
}
2024-07-19 11:52:12 +02:00
}
pub struct LinkRule {
re: [Regex; 1],
}
impl LinkRule {
pub fn new() -> Self {
2024-07-24 13:20:29 +02:00
Self {
re: [Regex::new(r"\[((?:\\.|[^\\\\])*?)\]\(((?:\\.|[^\\\\])*?)\)").unwrap()],
}
2024-07-19 11:52:12 +02:00
}
}
impl RegexRule for LinkRule {
fn name(&self) -> &'static str { "Link" }
fn regexes(&self) -> &[Regex] { &self.re }
2024-07-24 13:20:29 +02:00
fn on_regex_match<'a>(
&self,
_: usize,
parser: &dyn Parser,
document: &'a dyn Document,
token: Token,
matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
2024-07-19 11:52:12 +02:00
let mut result = vec![];
2024-07-24 13:20:29 +02:00
let link_name = match matches.get(1) {
Some(name) => {
if name.as_str().is_empty() {
2024-07-19 11:52:12 +02:00
result.push(
Report::build(ReportKind::Error, token.source(), name.start())
2024-07-24 13:20:29 +02:00
.with_message("Empty link name")
.with_label(
Label::new((token.source().clone(), name.range()))
.with_message("Link name is empty")
.with_color(parser.colors().error),
)
.finish(),
);
2024-07-19 11:52:12 +02:00
return result;
2024-07-24 13:20:29 +02:00
}
2024-07-19 11:52:12 +02:00
// TODO: process into separate document...
2024-07-24 13:20:29 +02:00
let text_content = util::process_text(document, name.as_str());
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
if text_content.as_str().is_empty() {
2024-07-19 11:52:12 +02:00
result.push(
Report::build(ReportKind::Error, token.source(), name.start())
2024-07-24 13:20:29 +02:00
.with_message("Empty link name")
.with_label(
Label::new((token.source(), name.range()))
.with_message(format!(
"Link name is empty. Once processed, `{}` yields `{}`",
name.as_str().fg(parser.colors().highlight),
text_content.as_str().fg(parser.colors().highlight),
))
.with_color(parser.colors().error),
)
.finish(),
);
2024-07-19 11:52:12 +02:00
return result;
2024-07-24 13:20:29 +02:00
}
2024-07-19 11:52:12 +02:00
text_content
2024-07-24 13:20:29 +02:00
}
_ => panic!("Empty link name"),
};
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
let link_url = match matches.get(2) {
Some(url) => {
if url.as_str().is_empty() {
2024-07-19 11:52:12 +02:00
result.push(
Report::build(ReportKind::Error, token.source(), url.start())
2024-07-24 13:20:29 +02:00
.with_message("Empty link url")
.with_label(
Label::new((token.source(), url.range()))
.with_message("Link url is empty")
.with_color(parser.colors().error),
)
.finish(),
);
2024-07-19 11:52:12 +02:00
return result;
2024-07-24 13:20:29 +02:00
}
let text_content = util::process_text(document, url.as_str());
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
if text_content.as_str().is_empty() {
2024-07-19 11:52:12 +02:00
result.push(
Report::build(ReportKind::Error, token.source(), url.start())
2024-07-24 13:20:29 +02:00
.with_message("Empty link url")
.with_label(
Label::new((token.source(), url.range()))
.with_message(format!(
"Link url is empty. Once processed, `{}` yields `{}`",
url.as_str().fg(parser.colors().highlight),
text_content.as_str().fg(parser.colors().highlight),
))
.with_color(parser.colors().error),
)
.finish(),
);
2024-07-19 11:52:12 +02:00
return result;
2024-07-24 13:20:29 +02:00
}
2024-07-19 11:52:12 +02:00
text_content
2024-07-24 13:20:29 +02:00
}
_ => panic!("Empty link url"),
};
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
parser.push(
document,
Box::new(Link::new(token.clone(), link_name, link_url)),
);
2024-07-19 11:52:12 +02:00
2024-07-24 13:20:29 +02:00
return result;
2024-07-19 11:52:12 +02:00
}
2024-07-21 15:56:56 +02:00
// TODO
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
2024-07-19 11:52:12 +02:00
}