1use std::{borrow::Cow, collections::{HashMap, HashSet}, fmt::{Display, Write}};
101
102use regex::{Captures, Regex};
103
104use crate::{error::{ParseError, Result}, expression::{Expression, ExpressionType}, expression_tokenizer::{Token, TokenType}};
105
106pub enum Local{
108    As(String),
110    This,
112    None
114}
115
116pub struct Scope{
118    pub opened: Box<dyn Block>,
120    pub depth: usize
122}
123
124enum PendingWrite<'a>{
126    Raw(&'a str),
128    Expression((Expression<'a>, &'static str, &'static str)),
130    Format((&'a str, &'a str, &'a str))
131}
132
133pub struct Rust{
135    pub using: HashSet<String>,
137    pub code: String
139}
140
141pub static USE_AS_DISPLAY: &str = "AsDisplay";
143pub static USE_AS_DISPLAY_HTML: &str = "AsDisplayHtml";
145
146pub struct Uses<'a>{
148    uses: &'a HashSet<String>
149}
150
151impl<'a> Display for Uses<'a>{
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        match self.uses.len(){
154            0 => (),
155            1 => write!(f, "use rusty_handlebars::{}", self.uses.iter().next().unwrap())?,
156            _ => {
157                f.write_str("use rusty_handlebars::")?;
158                let mut glue = '{';
159                for use_ in self.uses{
160                    f.write_char(glue)?;
161                    f.write_str(use_)?;
162                    glue = ',';
163                }
164                f.write_str("}")?;
165            }
166        }
167        Ok(())
168    }
169}
170
171impl Rust{
172    pub fn new() -> Self{
174        Self{
175            using: HashSet::new(),
176            code: String::new()
177        }
178    }
179
180    pub fn uses(&self) -> Uses{
182        Uses{ uses: &self.using}
183    }
184}
185
186pub trait Block{
188    fn handle_close<'a>(&self, rust: &mut Rust) {
190        rust.code.push_str("}");
191    }
192
193    fn resolve_private<'a>(&self, _depth: usize, expression: &'a Expression<'a>, _name: &str, _rust: &mut Rust) -> Result<()>{
195        Err(ParseError::new(&format!("{} not expected ", expression.content), expression))
196    }
197
198    fn handle_else<'a>(&self, expression: &'a Expression<'a>, _rust: &mut Rust) -> Result<()>{
200        Err(ParseError::new("else not expected here", expression))
201    }
202
203    fn this<'a>(&self) -> Option<&str>{
205        None
206    }
207
208    fn local<'a>(&self) -> &Local{
210        &Local::None
211    }
212}
213
214pub trait BlockFactory{
216    fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>>;
218}
219
220pub type BlockMap = HashMap<&'static str, &'static dyn BlockFactory>;
222
223pub struct Compile<'a>{
225    pub open_stack: Vec<Scope>,
227    pub block_map: &'a BlockMap
229}
230
231pub fn append_with_depth(depth: usize, var: &str, buffer: &mut String){
233    buffer.push_str(var);
234    buffer.push('_');
235    buffer.push_str(depth.to_string().as_str());
236}
237
238struct Root<'a>{
240    this: Option<&'a str>
241}
242
243impl<'a> Block for Root<'a>{
244    fn this<'b>(&self) -> Option<&str>{
245        self.this
246    }
247}
248
249impl<'a> Compile<'a>{
250    fn new(this: Option<&'static str>, block_map: &'a BlockMap) -> Self{
252        Self{
253            open_stack: vec![Scope{
254                depth: 0,
255                opened: Box::new(Root{this})
256            }],
257            block_map
258        }
259    }
260
261    fn find_scope(&self, var: &'a str) -> Result<(&'a str, &Scope)>{
263        let mut scope = self.open_stack.last().unwrap();
264        let mut local = var;
265        loop {
266            if local.starts_with("../"){
267                match scope.depth{
268                    0 => return Err(ParseError{ message: format!("unable to resolve scope for {}", var)}),
269                    _ => {
270                        local = &var[3 ..];
271                        scope = self.open_stack.get(scope.depth - 1).unwrap();
272                        continue;
273                    }
274                }
275            }
276            return Ok((local, scope));
277        }
278    }
279
280    fn resolve_local(&self, depth: usize, var: &'a str, local: &'a str, buffer: &mut String) -> bool{
282        if var.starts_with(local){
283            let len = local.len();
284            if var.len() > len{
285                if &var[len .. len + 1] != "."{
286                    return false;
287                }
288                append_with_depth(depth, local, buffer);
289                buffer.push_str(&var[len ..]);
290            }
291            else{
292                append_with_depth(depth, local, buffer);
293            }
294            return true;
295        }
296        return false;
297    }
298
299    fn resolve_var(&self, var: &'a str, scope: &Scope, buffer: &mut String) -> Result<()>{
301        if scope.depth == 0{
302            if let Some(this) = scope.opened.this(){
303                buffer.push_str(this);
304                buffer.push('.');
305            }
306            buffer.push_str(var);
307            return Ok(());
308        }
309        if match scope.opened.local(){
310            Local::As(local) => self.resolve_local(scope.depth, var, local, buffer),
311            Local::This => {
312                buffer.push_str("this_");
313                buffer.push_str(scope.depth.to_string().as_str());
314                if var != "this"{
315                    buffer.push('.');
316                    buffer.push_str(var);
317                }
318                true
319            },
320            Local::None => false
321        }{
322            return Ok(());
323        }
324        let parent = &self.open_stack[scope.depth - 1];
325        if let Some(this) = scope.opened.this(){
326            self.resolve_var(this, parent, buffer)?;
327            if var != this{
328                buffer.push('.');
329                buffer.push_str(var);
330            }
331        }
332        else{
333            self.resolve_var(var, parent, buffer)?;
334        }
335        return Ok(());
336    }
337
338    fn resolve_sub_expression(&self, raw: &str, value: &str, rust: &mut Rust) -> Result<()>{
340        self.resolve(&Expression { 
341            expression_type: ExpressionType::Raw,
342            prefix: "",
343            content: value,
344            postfix: "", 
345            raw
346        }, rust)
347    }
348
349    pub fn write_var(&self, expression: &Expression<'a>, rust: &mut Rust, var: &Token<'a>) -> Result<()>{
351        match var.token_type{
352            TokenType::PrivateVariable => {
353                let (name, scope) = self.find_scope(var.value)?;
354                scope.opened.resolve_private(scope.depth, expression, name, rust)?;
355            },
356            TokenType::Variable => {
357                let (name, scope) = self.find_scope(var.value)?;
358                self.resolve_var(name, scope, &mut rust.code)?;
359            },
360            TokenType::Literal => {
361                rust.code.push_str(var.value);
362            },
363            TokenType::SubExpression(raw) => {
364                self.resolve_sub_expression(raw, var.value, rust)?;
365            }
366        }
367        Ok(())
368    }
369
370    fn handle_else(&self, expression: &Expression<'a>, rust: &mut Rust) -> Result<()>{
372        match self.open_stack.last() {
373            Some(scope) => scope.opened.handle_else(expression, rust),
374            None => Err(ParseError::new("else not expected here", expression))
375        }
376    }
377
378    fn resolve_lookup(&self, expression: &Expression<'a>, prefix: &str, postfix: char, args: Token<'a>, rust: &mut Rust) -> Result<()>{
380        self.write_var(expression, rust, &args)?;
381        rust.code.push_str(prefix);
382        self.write_var(expression, rust, &args.next()?.ok_or(
383            ParseError::new("lookup expects 2 arguments", &expression))?
384        )?;
385        rust.code.push(postfix);
386        Ok(())
387    }
388
389    fn resolve_helper(&self, expression: &Expression<'a>, name: Token<'a>, mut args: Token<'a>, rust: &mut Rust) -> Result<()>{
391        match name.value{
392            "lookup" => self.resolve_lookup(expression, "[", ']', args, rust),
393            "try_lookup" => self.resolve_lookup(expression, ".get(", ')', args, rust),
394            name => {
395                rust.code.push_str(name);
396                rust.code.push('(');
397                self.write_var(expression, rust, &args)?;
398                loop {
399                    args = match args.next()?{
400                        Some(token) => {
401                            rust.code.push_str(", ");
402                            self.write_var(expression, rust, &token)?;
403                            token
404                        },
405                        None => {
406                            rust.code.push(')');
407                            return Ok(());
408                        }
409                    };
410                }
411            }
412        }
413    }
414
415    fn resolve(&self, expression: &Expression<'a>, rust: &mut Rust) -> Result<()>{
417        let token = match Token::first(&expression.content)?{
418            Some(token) => token,
419            None => return Err(ParseError::new("expected token", &expression))
420        };
421        rust.code.push_str(expression.prefix);
422        if let TokenType::SubExpression(raw) = token.token_type{
423            self.resolve_sub_expression(raw, token.value, rust)?;
424        }
425        else if let Some(args) = token.next()?{
426            self.resolve_helper(expression, token, args, rust)?;
427        }
428        else{
429            self.write_var(expression, rust, &token)?;
430        }
431        rust.code.push_str(expression.postfix);
432        Ok(())
433    }
434
435    pub fn write_local(&self, rust: &mut String, local: &Local){
437        append_with_depth(self.open_stack.len(), match local{
438            Local::As(local) => local,
439            _ => "this"
440        }, rust);
441    }
442
443    fn close(&mut self, expression: Expression<'a>, rust: &mut Rust) -> Result<()>{
445        let scope = self.open_stack.pop().ok_or_else(|| ParseError::new("Mismatched block helper", &expression))?;
446        Ok(scope.opened.handle_close(rust))
447    }
448
449    fn open(&mut self, expression: Expression<'a>, rust: &mut Rust) -> Result<()>{
451        let token = Token::first(&expression.content)?.ok_or_else(|| ParseError::new("expected token", &expression))?;
452        match self.block_map.get(token.value){
453            Some(block) => {
454                self.open_stack.push(Scope{
455                    opened: block.open(self, token, &expression, rust)?,
456                    depth: self.open_stack.len()
457                });
458                Ok(())
459            },
460            None => Err(ParseError::new(&format!("unsupported block helper {}", token.value), &expression))
461        }
462    }
463}
464
465#[derive(Debug, Clone, Copy)]
467pub struct Options{
468    pub root_var_name: Option<&'static str>,
470    pub write_var_name: &'static str
472}
473
474pub struct Compiler{
476    clean: Regex,
478    options: Options,
480    block_map: BlockMap
482}
483
484impl Compiler {
485    pub fn new(options: Options, block_map: BlockMap) -> Self{
487        Self{
488            clean: Regex::new("[\\\\\"\\{\\}]").unwrap(),
489            options,
490            block_map
491        }
492    }
493
494    fn escape<'a>(&self, content: &'a str) -> Cow<'a, str> {
496        self.clean.replace_all(
497            &content, |captures: &Captures| match &captures[0]{
498                "{" | "}" => format!("{}{}", &captures[0], &captures[0]),
499                _ => format!("\\{}", &captures[0])
500            }
501        )
502    }
503
504    fn commit_pending<'a>(&self, pending: &mut Vec<PendingWrite<'a>>, compile: &mut Compile<'a>, rust: &mut Rust) -> Result<()>{
506        if pending.is_empty(){
507            return Ok(());
508        }
509        rust.code.push_str("write!(");
510        rust.code.push_str(self.options.write_var_name);
511        rust.code.push_str(", \"");
512        for pending in pending.iter(){
513            match pending{
514                PendingWrite::Raw(raw) => rust.code.push_str(self.escape(raw).as_ref()),
515                PendingWrite::Expression(_) => rust.code.push_str("{}"),
516                PendingWrite::Format((_, format, _)) => rust.code.push_str(format)
517            }
518        }
519        rust.code.push('"');
520        for pending in pending.iter(){
521            match pending{
522                PendingWrite::Expression((expression, uses, display)) => {
523                    compile.resolve(&Expression{
524                        expression_type: ExpressionType::Raw,
525                        prefix: ", ",
526                        content: expression.content,
527                        postfix: display,
528                        raw: expression.raw
529                    }, rust)?;
530                    rust.using.insert(uses.to_string());
531                },
532                PendingWrite::Format((raw, _, content)) => {
533                    compile.resolve(&Expression{
534                        expression_type: ExpressionType::Raw,
535                        prefix: ", ",
536                        content,
537                        postfix: "",
538                        raw
539                    }, rust)?;
540                },
541                _ => ()
542            }
543        }
544        rust.code.push_str(")?;");
545        pending.clear();
546        Ok(())
547    }
548
549    fn select_write<'a>(expression: &Expression<'a>, uses: &'static str, postfix: &'static str) -> Result<PendingWrite<'a>>{
550        if let Some(token) = Token::first(&expression.content)?{
551            if let TokenType::Variable = token.token_type{
552                if token.value != "format"{
553                    return Ok(PendingWrite::Expression((expression.clone(), uses, postfix)));
554                }
555                let pattern = match token.next()?{
556                    Some(token) => token,
557                    _ => return Ok(PendingWrite::Expression((expression.clone(), uses, postfix)))
558                };
559                let value = match pattern.next(){
560                    Ok(Some(token)) => token,
561                    _ => return Err(ParseError::new("format requires 2 arguments", expression))
562                };
563                if let TokenType::Literal = pattern.token_type{
564                    if pattern.value.starts_with('"') && pattern.value.ends_with('"'){
565                        return Ok(PendingWrite::Format((expression.raw, &pattern.value[1..pattern.value.len() - 1], value.value)));
566                    }
567                }
568                return Err(ParseError::new("first argument of format must be a string literal", expression));
569            }
570        }
571        Ok(PendingWrite::Expression((expression.clone(), uses, postfix)))
572    }
573
574    pub fn compile(&self, src: &str) -> Result<Rust>{
576        let mut compile = Compile::new(self.options.root_var_name, &self.block_map);
577        let mut rust = Rust::new();
578        let mut pending: Vec<PendingWrite> = Vec::new();
579        let mut rest = src;
580        let mut expression = Expression::from(src)?;
581        while let Some(expr) = expression{
582            let Expression{
583                expression_type,
584                prefix,
585                content,
586                postfix,
587                raw: _
588            } = &expr;
589            rest = postfix; 
590            if !prefix.is_empty(){
591                pending.push(PendingWrite::Raw(prefix));
592            }
593            match expression_type{
594                ExpressionType::Raw => pending.push(Self::select_write(&expr, USE_AS_DISPLAY, ".as_display()")?),
595                ExpressionType::HtmlEscaped => if *content == "else" {
596                    self.commit_pending(&mut pending, &mut compile, &mut rust)?;
597                    compile.handle_else(&expr, &mut rust)?
598                } else {
599                    pending.push(Self::select_write(&expr, USE_AS_DISPLAY_HTML, ".as_display_html()")?)
600                },
601                ExpressionType::Open => {
602                    self.commit_pending(&mut pending, &mut compile, &mut rust)?;
603                    compile.open(expr, &mut rust)?
604                },
605                ExpressionType::Close => {
606                    self.commit_pending(&mut pending, &mut compile, &mut rust)?;
607                    compile.close(expr, &mut rust)?
608                },
609                ExpressionType::Escaped => pending.push(PendingWrite::Raw(content)),
610                _ => ()
611            };
612            expression = expr.next()?;
613        }
614        if !rest.is_empty(){
615            pending.push(PendingWrite::Raw(rest));
616        }
617        self.commit_pending(&mut pending, &mut compile, &mut rust)?;
618        Ok(rust)
619    }
620}