pub mod selector;
use selector::{Selector, SelectorState};
pub trait Template {
fn render(self) -> String;
}
#[derive(Debug, PartialEq, Eq)]
pub enum TokenType {
Comment,
RustString,
PushedRustString,
Selector,
Html,
Raw,
}
#[derive(Debug)]
pub struct Token {
pub r#type: TokenType,
pub raw: String,
pub html: String,
pub indent: i32,
pub line: i32,
pub selector: Option<SelectorState>,
}
impl Token {
pub fn from_indent_ln(indent: i32, line: i32) -> Self {
Self {
r#type: TokenType::Raw,
raw: "\n".to_string(),
html: "\n".to_string(),
indent,
line,
selector: None,
}
}
pub fn from_string(value: String, indent: i32, line: i32) -> Option<Self> {
let mut chars = value.chars();
match match chars.next() {
Some(c) => c,
None => {
return Some(Self::from_indent_ln(indent, line));
}
} {
'/' => {
if let Some(char) = chars.next() {
if char == '>' {
return Some(Self {
r#type: TokenType::Raw,
raw: value.clone(),
html: value,
indent,
line,
selector: None,
});
}
}
return Some(Self::from_indent_ln(indent, line));
}
'-' => {
let mut raw = String::new();
while let Some(char) = chars.next() {
raw.push(char);
}
return Some(Self {
r#type: TokenType::RustString,
raw,
html: String::new(),
indent,
line,
selector: None,
});
}
'=' => {
let mut raw = String::new();
while let Some(char) = chars.next() {
raw.push(char);
}
return Some(Self {
r#type: TokenType::PushedRustString,
raw,
html: String::new(),
indent,
line,
selector: None,
});
}
'%' => {
let mut raw = String::new();
let mut data = String::new();
let mut inline: bool = false;
let mut whitespace_sensitive: bool = false;
while let Some(char) = chars.next() {
if char == '\'' {
inline = true;
break;
} else if char == '~' {
whitespace_sensitive = true;
continue;
}
raw.push(char);
}
if inline {
while let Some(char) = chars.next() {
data.push(char);
}
}
let selector = Selector::new(raw.clone()).parse();
return Some(Self {
r#type: TokenType::Selector,
raw: format!("{raw}{data}"),
html: if inline {
format!("{}{data}</{}>", selector.clone().render(), selector.tag)
} else {
selector.clone().render()
},
indent: if whitespace_sensitive { -1 } else { indent },
line,
selector: Some(selector),
});
}
'@' => {
let mut raw = String::new();
while let Some(char) = chars.next() {
raw.push(char);
}
return Some(Self {
r#type: TokenType::Html,
raw: raw.clone(),
html: raw,
indent,
line,
selector: None,
});
}
_ => {
return Some(Self {
r#type: TokenType::Raw,
raw: value.clone(),
html: value,
indent,
line,
selector: None,
});
}
}
}
}
pub struct TokenStream(Parser);
impl Iterator for TokenStream {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
pub struct ParserState {
pub line_number: i32,
}
impl Default for ParserState {
fn default() -> Self {
Self { line_number: -1 }
}
}
pub struct Parser(Vec<String>, ParserState);
impl Parser {
pub fn new(input: String) -> Self {
let mut lines = Vec::new();
for line in input.split("\n") {
lines.push(line.to_owned())
}
Self(lines, ParserState::default())
}
pub fn parse(self) -> TokenStream {
TokenStream(self)
}
pub fn next(&mut self) -> Option<Token> {
self.1.line_number += 1;
let line = match self.0.get(self.1.line_number as usize) {
Some(l) => l,
None => return None,
};
if line.is_empty() {
return Some(Token::from_indent_ln(0, self.1.line_number));
}
let mut indent: i32 = 0;
let mut chars = line.chars();
while let Some(char) = chars.next() {
if (char != ' ') & (char != '\t') {
break;
}
indent += 1;
}
Token::from_string(line.trim().to_owned(), indent, self.1.line_number)
}
}