Update auto-registry

This commit is contained in:
ef3d0c3e 2024-12-09 15:27:30 +01:00
commit 4c6f41f9ff
24 changed files with 333 additions and 314 deletions

View file

@ -269,7 +269,7 @@ pub fn generate_registry(attr: TokenStream, input: TokenStream) -> TokenStream {
for name in names {
let struct_name: proc_macro2::TokenStream = name.parse().unwrap();
stream.extend(quote::quote_spanned!(proc_macro2::Span::call_site() =>
#struct_name::new(),
#struct_name::default(),
));
}
} else {

View file

@ -32,6 +32,7 @@ struct QuoteData {
}
#[derive(Debug)]
#[auto_registry::auto_registry(registry = "blocks")]
pub struct Quote {
properties: PropertyParser,
}

View file

@ -34,11 +34,30 @@ pub trait BlockType: core::fmt::Debug {
) -> Result<String, String>;
}
#[derive(Default)]
pub struct BlockHolder {
blocks: HashMap<String, Rc<dyn BlockType>>,
}
macro_rules! create_blocks {
( $($construct:expr),+ $(,)? ) => {{
let mut map = HashMap::new();
$(
let val = Rc::new($construct) as Rc<dyn BlockType>;
map.insert(val.name().to_string(), val);
)+
map
}};
}
#[auto_registry::generate_registry(registry = "blocks", target = make_blocks, return_type = HashMap<String, Rc<dyn BlockType>>, maker = create_blocks)]
impl Default for BlockHolder {
fn default() -> Self {
Self {
blocks: make_blocks()
}
}
}
impl BlockHolder {
pub fn get(&self, layout_name: &str) -> Option<Rc<dyn BlockType>> {
self.blocks.get(layout_name).cloned()

View file

@ -35,8 +35,8 @@ pub struct BlockRule {
continue_re: Regex,
}
impl BlockRule {
pub fn new() -> Self {
impl Default for BlockRule {
fn default() -> Self {
Self {
start_re: Regex::new(
r"(?:^|\n)>[^\S\r\n]*(?:\[!((?:\\.|[^\\\\])*?)\])(?:\[((?:\\.|[^\\\\])*?)\])?[^\S\r\n]*",

View file

@ -33,8 +33,8 @@ pub struct CodeRule {
properties: PropertyParser,
}
impl CodeRule {
pub fn new() -> Self {
impl Default for CodeRule {
fn default() -> Self {
let mut props = HashMap::new();
props.insert(
"line_offset".to_string(),
@ -71,14 +71,14 @@ impl RegexRule for CodeRule {
state: &ParserState,
document: &dyn Document,
token: Token,
matches: Captures,
captures: Captures,
) -> Vec<Report> {
let mut reports = vec![];
// Properties
let prop_source = escape_source(
token.source(),
matches.get(1).map_or(0..0, |m| m.range()),
captures.get(1).map_or(0..0, |m| m.range()),
"Code Properties".into(),
'\\',
"]",
@ -92,7 +92,7 @@ impl RegexRule for CodeRule {
None => return reports,
};
let code_lang = match matches.get(2) {
let code_lang = match captures.get(2) {
None => "Plain Text".to_string(),
Some(lang) => {
let mut code_lang = lang.as_str().trim_start().trim_end().to_string();
@ -124,13 +124,13 @@ impl RegexRule for CodeRule {
};
let mut code_content = if index == 0 {
util::escape_text('\\', "```", matches.get(4).unwrap().as_str(), false)
util::escape_text('\\', "```", captures.get(4).unwrap().as_str(), false)
} else {
util::escape_text(
'\\',
"``",
matches.get(3).unwrap().as_str(),
!matches.get(3).unwrap().as_str().contains('\n'),
captures.get(3).unwrap().as_str(),
!captures.get(3).unwrap().as_str().contains('\n'),
)
};
if code_content.bytes().last() == Some(b'\n')
@ -156,7 +156,7 @@ impl RegexRule for CodeRule {
if index == 0
// Block
{
let code_name = matches.get(3).and_then(|name| {
let code_name = captures.get(3).and_then(|name| {
let code_name = name.as_str().trim_end().trim_start().to_string();
(!code_name.is_empty()).then_some(code_name)
});
@ -182,12 +182,12 @@ impl RegexRule for CodeRule {
// Code Ranges
if let Some(coderanges) = CodeRange::from_source(token.source(), &state.shared.lsp) {
coderanges.add(matches.get(4).unwrap().range(), code_lang.clone());
coderanges.add(captures.get(4).unwrap().range(), code_lang.clone());
}
// Conceals
if let Some(conceals) = Conceals::from_source(token.source(), &state.shared.lsp) {
let range = matches
let range = captures
.get(0)
.map(|m| {
if token.source().content().as_bytes()[m.start()] == b'\n' {
@ -213,7 +213,7 @@ impl RegexRule for CodeRule {
},
);
let range = matches
let range = captures
.get(0)
.map(|m| {
if token.source().content().as_bytes()[m.start()] == b'\n' {
@ -253,7 +253,7 @@ impl RegexRule for CodeRule {
// Code Ranges
if let Some(coderanges) = CodeRange::from_source(token.source(), &state.shared.lsp) {
if block == CodeKind::MiniBlock {
let range = matches.get(3).unwrap().range();
let range = captures.get(3).unwrap().range();
coderanges.add(range.start + 1..range.end, code_lang.clone());
}
}
@ -261,7 +261,7 @@ impl RegexRule for CodeRule {
// Conceals
if let Some(conceals) = Conceals::from_source(token.source(), &state.shared.lsp) {
if block == CodeKind::MiniBlock {
let range = matches
let range = captures
.get(0)
.map(|m| {
if token.source().content().as_bytes()[m.start()] == b'\n' {
@ -287,7 +287,7 @@ impl RegexRule for CodeRule {
},
);
let range = matches
let range = captures
.get(0)
.map(|m| {
if token.source().content().as_bytes()[m.start()] == b'\n' {
@ -307,7 +307,7 @@ impl RegexRule for CodeRule {
// Semantic
if let Some((sems, tokens)) = Semantics::from_source(token.source(), &state.shared.lsp) {
let range = matches
let range = captures
.get(0)
.map(|m| {
if token.source().content().as_bytes()[m.start()] == b'\n' {
@ -321,15 +321,15 @@ impl RegexRule for CodeRule {
range.start..range.start + if index == 0 { 3 } else { 2 },
tokens.code_sep,
);
if let Some(props) = matches.get(1).map(|m| m.range()) {
if let Some(props) = captures.get(1).map(|m| m.range()) {
sems.add(props.start - 1..props.start, tokens.code_props_sep);
sems.add(props.end..props.end + 1, tokens.code_props_sep);
}
if let Some(lang) = matches.get(2).map(|m| m.range()) {
if let Some(lang) = captures.get(2).map(|m| m.range()) {
sems.add(lang.clone(), tokens.code_lang);
}
if index == 0 {
if let Some(title) = matches.get(3).map(|m| m.range()) {
if let Some(title) = captures.get(3).map(|m| m.range()) {
sems.add(title.clone(), tokens.code_title);
}
}

View file

@ -38,8 +38,8 @@ pub struct CommentRule {
re: [Regex; 1],
}
impl CommentRule {
pub fn new() -> Self {
impl Default for CommentRule {
fn default() -> Self {
Self {
re: [Regex::new(r"(?:(?:^|\n)|[^\S\n]+)::(.*)").unwrap()],
}

View file

@ -152,12 +152,9 @@ impl RuleState for CustomStyleState {
static STATE_NAME: &str = "elements.custom_style";
#[auto_registry::auto_registry(registry = "rules", path = "crate::elements::customstyle")]
#[derive(Default)]
pub struct CustomStyleRule;
impl CustomStyleRule {
pub fn new() -> Self { Self {} }
}
impl Rule for CustomStyleRule {
fn name(&self) -> &'static str { "Custom Style" }

View file

@ -25,35 +25,35 @@ pub struct ElemStyleRule {
start_re: Regex,
}
impl ElemStyleRule {
pub fn new() -> Self {
impl Default for ElemStyleRule {
fn default() -> Self {
Self {
start_re: Regex::new(r"(?:^|\n)@@(.*?)=\s*\{").unwrap(),
}
}
}
/// Finds the json substring inside aother string
pub fn json_substring(str: &str) -> Option<&str> {
let mut in_string = false;
let mut brace_depth = 0;
let mut escaped = false;
/// Finds the json substring inside aother string
fn json_substring(str: &str) -> Option<&str> {
let mut in_string = false;
let mut brace_depth = 0;
let mut escaped = false;
for (pos, c) in str.char_indices() {
match c {
'{' if !in_string => brace_depth += 1,
'}' if !in_string => brace_depth -= 1,
'\\' if in_string => escaped = !escaped,
'"' if !escaped => in_string = !in_string,
_ => escaped = false,
}
if brace_depth == 0 {
return Some(&str[..=pos]);
}
for (pos, c) in str.char_indices() {
match c {
'{' if !in_string => brace_depth += 1,
'}' if !in_string => brace_depth -= 1,
'\\' if in_string => escaped = !escaped,
'"' if !escaped => in_string = !in_string,
_ => escaped = false,
}
None
if brace_depth == 0 {
return Some(&str[..=pos]);
}
}
None
}
impl Rule for ElemStyleRule {
@ -127,7 +127,7 @@ impl Rule for ElemStyleRule {
};
// Get value
let new_style = match ElemStyleRule::json_substring(
let new_style = match json_substring(
&cursor.source.clone().content().as_str()[cursor.pos..],
) {
None => {

View file

@ -160,8 +160,8 @@ pub struct GraphRule {
properties: PropertyParser,
}
impl GraphRule {
pub fn new() -> Self {
impl Default for GraphRule {
fn default() -> Self {
let mut props = HashMap::new();
props.insert(
"layout".to_string(),

View file

@ -22,21 +22,21 @@ pub struct ImportRule {
re: [Regex; 1],
}
impl ImportRule {
pub fn new() -> Self {
impl Default for ImportRule {
fn default() -> Self {
Self {
re: [Regex::new(r"(?:^|\n)@import(?:\[(.*)\])?[^\S\r\n]+(.*)").unwrap()],
}
}
}
pub fn validate_name(_colors: &ReportColors, name: &str) -> Result<String, String> {
Ok(name.to_string())
}
fn validate_name(_colors: &ReportColors, name: &str) -> Result<String, String> {
Ok(name.to_string())
}
pub fn validate_as(_colors: &ReportColors, as_name: &str) -> Result<String, String> {
// TODO: Use variable name validation rules
Ok(as_name.to_string())
}
fn validate_as(_colors: &ReportColors, as_name: &str) -> Result<String, String> {
// TODO: Use variable name validation rules
Ok(as_name.to_string())
}
impl RegexRule for ImportRule {
@ -60,7 +60,7 @@ impl RegexRule for ImportRule {
// Path
let import_file = match matches.get(2) {
Some(name) => match ImportRule::validate_name(state.parser.colors(), name.as_str()) {
Some(name) => match validate_name(state.parser.colors(), name.as_str()) {
Err(msg) => {
report_err!(
&mut reports,
@ -121,7 +121,7 @@ impl RegexRule for ImportRule {
// [Optional] import as
let import_as = match matches.get(1) {
Some(as_name) => match ImportRule::validate_as(state.parser.colors(), as_name.as_str())
Some(as_name) => match validate_as(state.parser.colors(), as_name.as_str())
{
Ok(as_name) => as_name,
Err(msg) => {

View file

@ -340,8 +340,8 @@ pub struct LayoutRule {
re: [Regex; 3],
}
impl LayoutRule {
pub fn new() -> Self {
impl Default for LayoutRule {
fn default() -> Self {
Self {
re: [
RegexBuilder::new(
@ -365,44 +365,44 @@ impl LayoutRule {
],
}
}
}
pub fn initialize_state(state: &ParserState) -> Rc<RefCell<dyn RuleState>> {
let mut rule_state_borrow = state.shared.rule_state.borrow_mut();
match rule_state_borrow.get(STATE_NAME) {
Some(state) => state,
None => {
// Insert as a new state
match rule_state_borrow.insert(
STATE_NAME.into(),
Rc::new(RefCell::new(LayoutState { stack: vec![] })),
) {
Err(err) => panic!("{err}"),
Ok(state) => state,
}
pub fn initialize_state(state: &ParserState) -> Rc<RefCell<dyn RuleState>> {
let mut rule_state_borrow = state.shared.rule_state.borrow_mut();
match rule_state_borrow.get(STATE_NAME) {
Some(state) => state,
None => {
// Insert as a new state
match rule_state_borrow.insert(
STATE_NAME.into(),
Rc::new(RefCell::new(LayoutState { stack: vec![] })),
) {
Err(err) => panic!("{err}"),
Ok(state) => state,
}
}
}
}
pub fn parse_properties<'a>(
mut reports: &mut Vec<Report>,
state: &ParserState,
token: &Token,
layout_type: Rc<dyn LayoutType>,
m: Option<Match>,
) -> Option<Box<dyn Any>> {
let prop_source = escape_source(
token.source(),
m.map_or(0..0, |m| m.range()),
format!("Layout {} Properties", layout_type.name()),
'\\',
"]",
);
layout_type.parse_properties(
reports,
state,
Token::new(0..prop_source.content().len(), prop_source),
)
}
pub fn parse_properties<'a>(
mut reports: &mut Vec<Report>,
state: &ParserState,
token: &Token,
layout_type: Rc<dyn LayoutType>,
m: Option<Match>,
) -> Option<Box<dyn Any>> {
let prop_source = escape_source(
token.source(),
m.map_or(0..0, |m| m.range()),
format!("Layout {} Properties", layout_type.name()),
'\\',
"]",
);
layout_type.parse_properties(
reports,
state,
Token::new(0..prop_source.content().len(), prop_source),
)
}
static STATE_NAME: &str = "elements.layout";
@ -426,7 +426,7 @@ impl RegexRule for LayoutRule {
) -> Vec<Report> {
let mut reports = vec![];
let rule_state = LayoutRule::initialize_state(state);
let rule_state = initialize_state(state);
if index == 0
// BEGIN_LAYOUT
@ -504,7 +504,7 @@ impl RegexRule for LayoutRule {
};
// Parse properties
let properties = match LayoutRule::parse_properties(
let properties = match parse_properties(
&mut reports,
state,
&token,
@ -612,7 +612,7 @@ impl RegexRule for LayoutRule {
}
// Parse properties
let properties = match LayoutRule::parse_properties(
let properties = match parse_properties(
&mut reports,
state,
&token,
@ -698,7 +698,7 @@ impl RegexRule for LayoutRule {
}
// Parse properties
let properties = match LayoutRule::parse_properties(
let properties = match parse_properties(
&mut reports,
state,
&token,
@ -774,7 +774,7 @@ impl RegexRule for LayoutRule {
CTX.with_borrow_mut(|ctx| {
ctx.as_mut().map(|ctx| {
// Make sure the rule state has been initialized
let rule_state = LayoutRule::initialize_state(ctx.state);
let rule_state = initialize_state(ctx.state);
// Get layout
//

View file

@ -82,8 +82,8 @@ pub struct LinkRule {
re: [Regex; 1],
}
impl LinkRule {
pub fn new() -> Self {
impl Default for LinkRule {
fn default() -> Self {
Self {
re: [Regex::new(r"\[((?:\\.|[^\\\\])*?)\]\(((?:\\.|[^\\\\])*?)\)").unwrap()],
}

View file

@ -165,8 +165,8 @@ pub struct ListRule {
properties: PropertyParser,
}
impl ListRule {
pub fn new() -> Self {
impl Default for ListRule {
fn default() -> Self {
let mut props = HashMap::new();
props.insert(
"offset".to_string(),
@ -184,97 +184,97 @@ impl ListRule {
properties: PropertyParser { properties: props },
}
}
}
fn push_markers(
token: &Token,
state: &ParserState,
document: &dyn Document,
current: &Vec<(bool, usize)>,
target: &Vec<(bool, usize)>,
) {
let mut start_pos = 0;
for i in 0..std::cmp::min(target.len(), current.len()) {
if current[i].0 != target[i].0 {
break;
}
start_pos += 1;
fn push_markers(
token: &Token,
state: &ParserState,
document: &dyn Document,
current: &Vec<(bool, usize)>,
target: &Vec<(bool, usize)>,
) {
let mut start_pos = 0;
for i in 0..std::cmp::min(target.len(), current.len()) {
if current[i].0 != target[i].0 {
break;
}
// Close
for i in start_pos..current.len() {
state.push(
document,
Box::new(ListMarker {
location: token.clone(),
kind: MarkerKind::Close,
numbered: current[current.len() - 1 - (i - start_pos)].0,
}),
);
}
// Open
for i in start_pos..target.len() {
state.push(
document,
Box::new(ListMarker {
location: token.clone(),
kind: MarkerKind::Open,
numbered: target[i].0,
}),
);
}
start_pos += 1;
}
fn parse_depth(depth: &str, document: &dyn Document, offset: usize) -> Vec<(bool, usize)> {
let mut parsed = vec![];
let prev_entry = document
.last_element::<ListEntry>()
.and_then(|entry| Ref::filter_map(entry, |e| Some(&e.numbering)).ok());
// Close
for i in start_pos..current.len() {
state.push(
document,
Box::new(ListMarker {
location: token.clone(),
kind: MarkerKind::Close,
numbered: current[current.len() - 1 - (i - start_pos)].0,
}),
);
}
let mut continue_match = true;
depth.chars().enumerate().for_each(|(idx, c)| {
let number = if offset == usize::MAX || idx + 1 != depth.len() {
prev_entry
.as_ref()
.and_then(|v| {
if !continue_match {
return None;
}
let numbered = c == '-';
// Open
for i in start_pos..target.len() {
state.push(
document,
Box::new(ListMarker {
location: token.clone(),
kind: MarkerKind::Open,
numbered: target[i].0,
}),
);
}
}
match v.get(idx) {
None => None,
Some((prev_numbered, prev_idx)) => {
if *prev_numbered != numbered {
continue_match = false;
None
}
// New depth
else if idx + 1 == v.len() {
Some(*prev_idx + 1)
}
// Increase from previous
else {
Some(*prev_idx)
} // Do nothing
fn parse_depth(depth: &str, document: &dyn Document, offset: usize) -> Vec<(bool, usize)> {
let mut parsed = vec![];
let prev_entry = document
.last_element::<ListEntry>()
.and_then(|entry| Ref::filter_map(entry, |e| Some(&e.numbering)).ok());
let mut continue_match = true;
depth.chars().enumerate().for_each(|(idx, c)| {
let number = if offset == usize::MAX || idx + 1 != depth.len() {
prev_entry
.as_ref()
.and_then(|v| {
if !continue_match {
return None;
}
let numbered = c == '-';
match v.get(idx) {
None => None,
Some((prev_numbered, prev_idx)) => {
if *prev_numbered != numbered {
continue_match = false;
None
}
// New depth
else if idx + 1 == v.len() {
Some(*prev_idx + 1)
}
// Increase from previous
else {
Some(*prev_idx)
} // Do nothing
}
})
.unwrap_or(1)
} else {
offset
};
}
})
.unwrap_or(1)
} else {
offset
};
match c {
'*' => parsed.push((false, number)),
'-' => parsed.push((true, number)),
_ => panic!("Unimplemented"),
}
});
match c {
'*' => parsed.push((false, number)),
'-' => parsed.push((true, number)),
_ => panic!("Unimplemented"),
}
});
parsed
}
parsed
}
impl Rule for ListRule {
@ -358,7 +358,7 @@ impl Rule for ListRule {
};
// Depth
let depth = ListRule::parse_depth(
let depth = parse_depth(
captures.get(1).unwrap().as_str(),
document,
offset.unwrap_or(usize::MAX),
@ -506,9 +506,9 @@ impl Rule for ListRule {
.last_element::<ListEntry>()
.map(|ent| ent.numbering.clone())
{
ListRule::push_markers(&token, state, document, &previous_depth, &depth);
push_markers(&token, state, document, &previous_depth, &depth);
} else {
ListRule::push_markers(&token, state, document, &vec![], &depth);
push_markers(&token, state, document, &vec![], &depth);
}
state.push(
@ -535,7 +535,7 @@ impl Rule for ListRule {
.map(|ent| ent.numbering.clone())
.unwrap();
let token = Token::new(end_cursor.pos..end_cursor.pos, end_cursor.source.clone());
ListRule::push_markers(&token, state, document, &current, &Vec::new());
push_markers(&token, state, document, &current, &Vec::new());
(end_cursor, reports)
}

View file

@ -237,8 +237,8 @@ pub struct MediaRule {
properties: PropertyParser,
}
impl MediaRule {
pub fn new() -> Self {
impl Default for MediaRule {
fn default() -> Self {
let mut props = HashMap::new();
props.insert(
"type".to_string(),
@ -263,30 +263,31 @@ impl MediaRule {
}
}
fn validate_uri(uri: &str) -> Result<&str, String> {
let trimmed = uri.trim_start().trim_end();
}
if trimmed.is_empty() {
return Err("URIs is empty".to_string());
}
fn validate_uri(uri: &str) -> Result<&str, String> {
let trimmed = uri.trim_start().trim_end();
Ok(trimmed)
if trimmed.is_empty() {
return Err("URIs is empty".to_string());
}
fn detect_filetype(filename: &str) -> Option<MediaType> {
let sep = match filename.rfind('.') {
Some(pos) => pos,
None => return None,
};
Ok(trimmed)
}
// TODO: https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
match filename.split_at(sep + 1).1.to_ascii_lowercase().as_str() {
"png" | "apng" | "avif" | "gif" | "webp" | "svg" | "bmp" | "jpg" | "jpeg" | "jfif"
fn detect_filetype(filename: &str) -> Option<MediaType> {
let sep = match filename.rfind('.') {
Some(pos) => pos,
None => return None,
};
// TODO: https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
match filename.split_at(sep + 1).1.to_ascii_lowercase().as_str() {
"png" | "apng" | "avif" | "gif" | "webp" | "svg" | "bmp" | "jpg" | "jpeg" | "jfif"
| "pjpeg" | "pjp" => Some(MediaType::IMAGE),
"mp4" | "m4v" | "webm" | "mov" => Some(MediaType::VIDEO),
"mp3" | "ogg" | "flac" | "wav" => Some(MediaType::AUDIO),
_ => None,
}
}
}
@ -327,7 +328,7 @@ impl RegexRule for MediaRule {
let uri = match (
matches.get(2).unwrap(),
MediaRule::validate_uri(matches.get(2).unwrap().as_str()),
validate_uri(matches.get(2).unwrap().as_str()),
) {
(_, Ok(uri)) => util::escape_text('\\', ")", uri, true),
(m, Err(err)) => {
@ -372,7 +373,7 @@ impl RegexRule for MediaRule {
) {
(Some(media_type), Some(caption), Some(width)) => {
if media_type.is_none() {
match Self::detect_filetype(uri.as_str()) {
match detect_filetype(uri.as_str()) {
None => {
report_err!(
&mut reports,
@ -509,7 +510,7 @@ mod tests {
#[test]
fn regex() {
let rule = MediaRule::new();
let rule = MediaRule::default();
let re = &rule.regexes()[0];
assert!(re.is_match("![refname](some path...)[some properties] some description"));

View file

@ -92,8 +92,8 @@ pub struct ParagraphRule {
re: Regex,
}
impl ParagraphRule {
pub fn new() -> Self {
impl Default for ParagraphRule {
fn default() -> Self {
Self {
re: Regex::new(r"\n{2,}").unwrap(),
}

View file

@ -53,8 +53,8 @@ pub struct RawRule {
properties: PropertyParser,
}
impl RawRule {
pub fn new() -> Self {
impl Default for RawRule {
fn default() -> Self {
let mut props = HashMap::new();
props.insert(
"kind".to_string(),

View file

@ -161,8 +161,8 @@ pub struct ReferenceRule {
properties: PropertyParser,
}
impl ReferenceRule {
pub fn new() -> Self {
impl Default for ReferenceRule {
fn default() -> Self {
let mut props = HashMap::new();
props.insert(
"caption".to_string(),

View file

@ -29,7 +29,28 @@ pub struct ScriptRule {
}
impl ScriptRule {
pub fn new() -> Self {
fn validate_kind(&self, colors: &ReportColors, kind: &str) -> Result<usize, String> {
match self
.eval_kinds
.iter()
.position(|(kind_symbol, _)| kind == *kind_symbol)
{
Some(id) => Ok(id),
None => Err(format!(
"Unable to find eval kind `{}`. Available kinds:{}",
kind.fg(colors.highlight),
self.eval_kinds
.iter()
.fold(String::new(), |out, (symbol, name)| {
out + format!("\n - '{symbol}' => {name}").as_str()
})
)),
}
}
}
impl Default for ScriptRule {
fn default() -> Self {
Self {
re: [
Regex::new(r"(?:^|\n)@<(?:(.*)\n?)((?:\\.|[^\\\\])*?)(?:\n?)>@").unwrap(),
@ -43,39 +64,20 @@ impl ScriptRule {
],
}
}
}
fn validate_kernel_name(colors: &ReportColors, name: &str) -> Result<String, String> {
let trimmed = name.trim_end().trim_start();
if trimmed.is_empty() {
return Ok("main".to_string());
} else if trimmed.find(|c: char| c.is_whitespace()).is_some() {
return Err(format!(
fn validate_kernel_name(colors: &ReportColors, name: &str) -> Result<String, String> {
let trimmed = name.trim_end().trim_start();
if trimmed.is_empty() {
return Ok("main".to_string());
} else if trimmed.find(|c: char| c.is_whitespace()).is_some() {
return Err(format!(
"Kernel name `{}` contains whitespaces",
trimmed.fg(colors.highlight)
));
}
Ok(trimmed.to_string())
));
}
fn validate_kind(&self, colors: &ReportColors, kind: &str) -> Result<usize, String> {
match self
.eval_kinds
.iter()
.position(|(kind_symbol, _)| kind == *kind_symbol)
{
Some(id) => Ok(id),
None => Err(format!(
"Unable to find eval kind `{}`. Available kinds:{}",
kind.fg(colors.highlight),
self.eval_kinds
.iter()
.fold(String::new(), |out, (symbol, name)| {
out + format!("\n - '{symbol}' => {name}").as_str()
})
)),
}
}
Ok(trimmed.to_string())
}
impl RegexRule for ScriptRule {
@ -100,7 +102,7 @@ impl RegexRule for ScriptRule {
let kernel_name = match matches.get(1) {
None => "main".to_string(),
Some(name) => {
match ScriptRule::validate_kernel_name(state.parser.colors(), name.as_str()) {
match validate_kernel_name(state.parser.colors(), name.as_str()) {
Ok(name) => name,
Err(e) => {
report_err!(

View file

@ -150,8 +150,8 @@ pub struct SectionRule {
re: [Regex; 1],
}
impl SectionRule {
pub fn new() -> Self {
impl Default for SectionRule {
fn default() -> Self {
Self {
re: [Regex::new(r"(?:^|\n)(#{1,})(?:\{(.*)\})?((\*|\+){1,})?(.*)").unwrap()],
}

View file

@ -137,8 +137,8 @@ pub struct StyleRule {
re: [Regex; 4],
}
impl StyleRule {
pub fn new() -> Self {
impl Default for StyleRule {
fn default() -> Self {
Self {
re: [
// Bold

View file

@ -229,8 +229,8 @@ pub struct TexRule {
properties: PropertyParser,
}
impl TexRule {
pub fn new() -> Self {
impl Default for TexRule {
fn default() -> Self {
let mut props = HashMap::new();
props.insert(
"env".to_string(),

View file

@ -41,12 +41,9 @@ impl Element for Text {
}
#[auto_registry::auto_registry(registry = "rules", path = "crate::elements::text")]
#[derive(Default)]
pub struct TextRule;
impl TextRule {
pub fn new() -> Self { Self {} }
}
impl Rule for TextRule {
fn name(&self) -> &'static str { "Text" }

View file

@ -126,8 +126,8 @@ pub struct TocRule {
re: [Regex; 1],
}
impl TocRule {
pub fn new() -> Self {
impl Default for TocRule {
fn default() -> Self {
Self {
re: [
RegexBuilder::new(r"(?:^|\n)(?:[^\S\n]*)#\+TABLE_OF_CONTENT(.*)")

View file

@ -45,13 +45,6 @@ pub struct VariableRule {
}
impl VariableRule {
pub fn new() -> Self {
Self {
re: [Regex::new(r"(?:^|\n)@(')?(.*?)=((?:\\\n|.)*)").unwrap()],
kinds: vec![("".into(), "Regular".into()), ("'".into(), "Path".into())],
}
}
pub fn make_variable(
&self,
colors: &ReportColors,
@ -63,64 +56,73 @@ impl VariableRule {
) -> Result<Rc<dyn Variable>, String> {
match self.kinds[kind].0.as_str() {
"" => Ok(Rc::new(BaseVariable::new(
location,
name,
value_token,
value,
location,
name,
value_token,
value,
))),
"'" => {
match std::fs::canonicalize(value.as_str()) // TODO: not canonicalize
{
Ok(path) => Ok(Rc::new(PathVariable::new(location, name, value_token, path))),
Err(e) => Err(format!("Unable to canonicalize path `{}`: {}",
value.fg(colors.highlight),
e))
}
{
Ok(path) => Ok(Rc::new(PathVariable::new(location, name, value_token, path))),
Err(e) => Err(format!("Unable to canonicalize path `{}`: {}",
value.fg(colors.highlight),
e))
}
}
_ => panic!("Unhandled variable kind"),
}
}
}
// Trim and check variable name for validity
pub fn validate_name<'a>(
colors: &ReportColors,
original_name: &'a str,
) -> Result<&'a str, String> {
let name = original_name.trim_start().trim_end();
if name.contains("%") {
return Err(format!("Name cannot contain '{}'", "%".fg(colors.info)));
impl Default for VariableRule {
fn default() -> Self {
Self {
re: [Regex::new(r"(?:^|\n)@(')?(.*?)=((?:\\\n|.)*)").unwrap()],
kinds: vec![("".into(), "Regular".into()), ("'".into(), "Path".into())],
}
Ok(name)
}
}
pub fn validate_value(original_value: &str) -> Result<String, String> {
let mut escaped = 0usize;
let mut result = String::new();
for c in original_value.trim_start().trim_end().chars() {
if c == '\\' {
escaped += 1
} else if c == '\n' {
match escaped {
0 => return Err("Unknown error wile capturing value".to_string()),
// Remove '\n'
1 => {}
// Insert '\n'
_ => {
result.push(c);
(0..escaped - 2).for_each(|_| result.push('\\'));
}
// Trim and check variable name for validity
pub fn validate_name<'a>(
colors: &ReportColors,
original_name: &'a str,
) -> Result<&'a str, String> {
let name = original_name.trim_start().trim_end();
if name.contains("%") {
return Err(format!("Name cannot contain '{}'", "%".fg(colors.info)));
}
Ok(name)
}
pub fn validate_value(original_value: &str) -> Result<String, String> {
let mut escaped = 0usize;
let mut result = String::new();
for c in original_value.trim_start().trim_end().chars() {
if c == '\\' {
escaped += 1
} else if c == '\n' {
match escaped {
0 => return Err("Unknown error wile capturing value".to_string()),
// Remove '\n'
1 => {}
// Insert '\n'
_ => {
result.push(c);
(0..escaped - 2).for_each(|_| result.push('\\'));
}
escaped = 0;
} else {
(0..escaped).for_each(|_| result.push('\\'));
escaped = 0;
result.push(c);
}
escaped = 0;
} else {
(0..escaped).for_each(|_| result.push('\\'));
escaped = 0;
result.push(c);
}
(0..escaped).for_each(|_| result.push('\\'));
Ok(result)
}
(0..escaped).for_each(|_| result.push('\\'));
Ok(result)
}
impl RegexRule for VariableRule {
@ -188,7 +190,7 @@ impl RegexRule for VariableRule {
};
let var_name = match matches.get(2) {
Some(name) => match VariableRule::validate_name(state.parser.colors(), name.as_str()) {
Some(name) => match validate_name(state.parser.colors(), name.as_str()) {
Ok(var_name) => var_name,
Err(msg) => {
report_err!(
@ -211,7 +213,7 @@ impl RegexRule for VariableRule {
};
let (val_token, var_value) = match matches.get(3) {
Some(value) => match VariableRule::validate_value(value.as_str()) {
Some(value) => match validate_value(value.as_str()) {
Ok(var_value) => (Token::new(value.range(), token.source()), var_value),
Err(msg) => {
report_err!(
@ -326,8 +328,8 @@ pub struct VariableSubstitutionRule {
re: [Regex; 1],
}
impl VariableSubstitutionRule {
pub fn new() -> Self {
impl Default for VariableSubstitutionRule {
fn default() -> Self {
Self {
re: [Regex::new(r"%(.*?)%").unwrap()],
}
@ -400,7 +402,7 @@ impl RegexRule for VariableSubstitutionRule {
return reports;
}
// Invalid name
if let Err(msg) = VariableRule::validate_name(state.parser.colors(), name.as_str())
if let Err(msg) = validate_name(state.parser.colors(), name.as_str())
{
report_err!(
&mut reports,