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}