snekdown 0.33.4

A parser for the custom snekdown markdown syntax
Documentation
/*
 * Snekdown - Custom Markdown flavour and parser
 * Copyright (C) 2021  Trivernis
 * See LICENSE for more information.
 */

use crate::elements::{Block, Element, Inline, Line, ListItem};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

pub trait FreezeVariables {
    fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>>;
}

pub trait GetTemplateVariables {
    fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>>;
}

#[derive(Clone, Debug)]
pub struct Template {
    pub(crate) text: Vec<Element>,
    pub(crate) variables: HashMap<String, Arc<RwLock<TemplateVariable>>>,
}

#[derive(Clone, Debug)]
pub struct TemplateVariable {
    pub(crate) prefix: String,
    pub(crate) name: String,
    pub(crate) suffix: String,
    pub(crate) value: Option<Element>,
}

impl Template {
    pub fn render(&self, replacements: HashMap<String, Element>) -> Vec<Element> {
        replacements.iter().for_each(|(k, r)| {
            if let Some(v) = self.variables.get(k) {
                v.write().unwrap().set_value(r.clone())
            }
        });
        let elements = self
            .text
            .iter()
            .map(|e| {
                let mut e = e.clone();
                if let Some(template) = e.freeze_variables() {
                    Element::Inline(Box::new(Inline::TemplateVar(template)))
                } else {
                    e
                }
            })
            .collect();
        self.variables
            .iter()
            .for_each(|(_, v)| v.write().unwrap().reset());

        elements
    }
}

impl TemplateVariable {
    pub fn set_value(&mut self, value: Element) {
        self.value = Some(value)
    }
    pub fn reset(&mut self) {
        self.value = None
    }
}

impl GetTemplateVariables for Inline {
    fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>> {
        match self {
            Inline::TemplateVar(temp) => vec![Arc::clone(temp)],
            Inline::Colored(col) => col.value.get_template_variables(),
            Inline::Superscript(sup) => sup
                .value
                .iter()
                .map(|e| e.get_template_variables())
                .flatten()
                .collect(),
            Inline::Striked(striked) => striked
                .value
                .iter()
                .map(|e| e.get_template_variables())
                .flatten()
                .collect(),
            Inline::Underlined(under) => under
                .value
                .iter()
                .map(|e| e.get_template_variables())
                .flatten()
                .collect(),
            Inline::Italic(it) => it
                .value
                .iter()
                .map(|e| e.get_template_variables())
                .flatten()
                .collect(),
            Inline::Bold(bo) => bo
                .value
                .iter()
                .map(|e| e.get_template_variables())
                .flatten()
                .collect(),
            _ => Vec::new(),
        }
    }
}

impl GetTemplateVariables for Line {
    fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>> {
        match self {
            Line::Text(line) => line
                .subtext
                .iter()
                .map(|s| s.get_template_variables())
                .flatten()
                .collect(),
            Line::Centered(center) => center
                .line
                .subtext
                .iter()
                .map(|s| s.get_template_variables())
                .flatten()
                .collect(),
            _ => Vec::new(),
        }
    }
}

impl GetTemplateVariables for Block {
    fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>> {
        match self {
            Block::Section(sec) => sec
                .elements
                .iter()
                .map(|e| e.get_template_variables())
                .flatten()
                .collect(),
            Block::Paragraph(par) => par
                .elements
                .iter()
                .map(|l| l.get_template_variables())
                .flatten()
                .collect(),
            Block::Quote(q) => q
                .text
                .iter()
                .map(|t| {
                    t.subtext
                        .iter()
                        .map(|i| i.get_template_variables())
                        .flatten()
                })
                .flatten()
                .collect(),
            Block::List(list) => list
                .items
                .iter()
                .map(|item| item.get_template_variables())
                .flatten()
                .collect(),
            _ => Vec::new(),
        }
    }
}

impl GetTemplateVariables for Element {
    fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>> {
        match self {
            Element::Block(block) => block.get_template_variables(),
            Element::Inline(inline) => inline.get_template_variables(),
            Element::Line(line) => line.get_template_variables(),
        }
    }
}

impl FreezeVariables for Inline {
    fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>> {
        match self {
            Inline::TemplateVar(temp) => {
                let temp = temp.read().unwrap();
                return Some(Arc::new(RwLock::new((*temp).clone())));
            }
            Inline::Colored(col) => {
                if let Some(temp) = col.value.freeze_variables() {
                    col.value = Box::new(Inline::TemplateVar(temp))
                }
            }

            Inline::Superscript(sup) => {
                sup.value = sup
                    .value
                    .iter_mut()
                    .map(|e| {
                        if let Some(temp) = e.freeze_variables() {
                            Inline::TemplateVar(temp)
                        } else {
                            e.clone()
                        }
                    })
                    .collect();
            }
            Inline::Striked(striked) => {
                striked.value = striked
                    .value
                    .iter_mut()
                    .map(|e| {
                        if let Some(temp) = e.freeze_variables() {
                            Inline::TemplateVar(temp)
                        } else {
                            e.clone()
                        }
                    })
                    .collect();
            }
            Inline::Underlined(under) => {
                under.value = under
                    .value
                    .iter_mut()
                    .map(|e| {
                        if let Some(temp) = e.freeze_variables() {
                            Inline::TemplateVar(temp)
                        } else {
                            e.clone()
                        }
                    })
                    .collect();
            }
            Inline::Italic(it) => {
                it.value = it
                    .value
                    .iter_mut()
                    .map(|e| {
                        if let Some(temp) = e.freeze_variables() {
                            Inline::TemplateVar(temp)
                        } else {
                            e.clone()
                        }
                    })
                    .collect();
            }
            Inline::Bold(bo) => {
                bo.value = bo
                    .value
                    .iter_mut()
                    .map(|e| {
                        if let Some(temp) = e.freeze_variables() {
                            Inline::TemplateVar(temp)
                        } else {
                            e.clone()
                        }
                    })
                    .collect();
            }
            _ => {}
        }
        None
    }
}

impl GetTemplateVariables for ListItem {
    fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>> {
        let mut inner_vars: Vec<Arc<RwLock<TemplateVariable>>> = self
            .children
            .iter()
            .map(|child| child.get_template_variables())
            .flatten()
            .collect();
        inner_vars.append(&mut self.text.get_template_variables());

        inner_vars
    }
}

impl FreezeVariables for Line {
    fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>> {
        match self {
            Line::Text(text) => {
                text.subtext = text
                    .subtext
                    .iter_mut()
                    .map(|i| {
                        if let Some(t) = i.freeze_variables() {
                            Inline::TemplateVar(t)
                        } else {
                            (*i).clone()
                        }
                    })
                    .collect()
            }
            Line::Centered(center) => {
                center.line.subtext = center
                    .line
                    .subtext
                    .iter_mut()
                    .map(|i| {
                        if let Some(t) = i.freeze_variables() {
                            Inline::TemplateVar(t)
                        } else {
                            (*i).clone()
                        }
                    })
                    .collect()
            }
            _ => {}
        }
        None
    }
}

impl FreezeVariables for Block {
    fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>> {
        match self {
            Block::Section(s) => s.elements.iter_mut().for_each(|b| {
                b.freeze_variables();
            }),
            Block::Paragraph(p) => p.elements.iter_mut().for_each(|l| {
                l.freeze_variables();
            }),
            Block::Quote(q) => q.text.iter_mut().for_each(|t| {
                t.subtext = t
                    .subtext
                    .iter_mut()
                    .map(|i| {
                        if let Some(t) = i.freeze_variables() {
                            Inline::TemplateVar(t)
                        } else {
                            (*i).clone()
                        }
                    })
                    .collect()
            }),
            Block::List(list) => list.items.iter_mut().for_each(|item| {
                item.freeze_variables();
            }),
            _ => {}
        };

        None
    }
}

impl FreezeVariables for Element {
    fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>> {
        match self {
            Element::Block(b) => b.freeze_variables(),
            Element::Line(l) => l.freeze_variables(),
            Element::Inline(i) => return i.freeze_variables(),
        };

        None
    }
}

impl FreezeVariables for ListItem {
    fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>> {
        self.children.iter_mut().for_each(|child| {
            child.freeze_variables();
        });
        self.text.freeze_variables();
        None
    }
}