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 while local.starts_with("../"){
266 match scope.depth{
267 0 => return Err(ParseError{ message: format!("unable to resolve scope for {}", var)}),
268 _ => {
269 local = &var[3 ..];
270 scope = self.open_stack.get(scope.depth - 1).unwrap();
271 }
272 }
273 }
274 return Ok((local, scope));
275 }
276
277 fn resolve_local(&self, depth: usize, var: &'a str, local: &'a str, buffer: &mut String) -> bool{
279 if var.starts_with(local){
280 let len = local.len();
281 if var.len() > len{
282 if &var[len .. len + 1] != "."{
283 return false;
284 }
285 append_with_depth(depth, local, buffer);
286 buffer.push_str(&var[len ..]);
287 }
288 else{
289 append_with_depth(depth, local, buffer);
290 }
291 return true;
292 }
293 return false;
294 }
295
296 fn resolve_var(&self, var: &'a str, scope: &Scope, buffer: &mut String) -> Result<()>{
298 if scope.depth == 0{
299 if let Some(this) = scope.opened.this(){
300 buffer.push_str(this);
301 buffer.push('.');
302 }
303 buffer.push_str(var);
304 return Ok(());
305 }
306 if match scope.opened.local(){
307 Local::As(local) => self.resolve_local(scope.depth, var, local, buffer),
308 Local::This => {
309 buffer.push_str("this_");
310 buffer.push_str(scope.depth.to_string().as_str());
311 if var != "this"{
312 buffer.push('.');
313 buffer.push_str(var);
314 }
315 true
316 },
317 Local::None => false
318 }{
319 return Ok(());
320 }
321 let parent = &self.open_stack[scope.depth - 1];
322 if let Some(this) = scope.opened.this(){
323 self.resolve_var(this, parent, buffer)?;
324 if var != this{
325 buffer.push('.');
326 buffer.push_str(var);
327 }
328 }
329 else{
330 self.resolve_var(var, parent, buffer)?;
331 }
332 Ok(())
333 }
334
335 fn resolve_sub_expression(&self, raw: &str, value: &str, rust: &mut Rust) -> Result<()>{
337 self.resolve(&Expression {
338 expression_type: ExpressionType::Raw,
339 prefix: "",
340 content: value,
341 postfix: "",
342 raw
343 }, rust)
344 }
345
346 pub fn write_var(&self, expression: &Expression<'a>, rust: &mut Rust, var: &Token<'a>) -> Result<()>{
348 match var.token_type{
349 TokenType::PrivateVariable => {
350 let (name, scope) = self.find_scope(var.value)?;
351 scope.opened.resolve_private(scope.depth, expression, name, rust)?;
352 },
353 TokenType::Variable => {
354 let (name, scope) = self.find_scope(var.value)?;
355 self.resolve_var(name, scope, &mut rust.code)?;
356 },
357 TokenType::Literal => {
358 rust.code.push_str(var.value);
359 },
360 TokenType::SubExpression(raw) => {
361 self.resolve_sub_expression(raw, var.value, rust)?;
362 }
363 }
364 Ok(())
365 }
366
367 fn handle_else(&self, expression: &Expression<'a>, rust: &mut Rust) -> Result<()>{
369 match self.open_stack.last() {
370 Some(scope) => scope.opened.handle_else(expression, rust),
371 None => Err(ParseError::new("else not expected here", expression))
372 }
373 }
374
375 fn resolve_lookup(&self, expression: &Expression<'a>, prefix: &str, postfix: char, args: Token<'a>, rust: &mut Rust) -> Result<()>{
377 self.write_var(expression, rust, &args)?;
378 rust.code.push_str(prefix);
379 self.write_var(expression, rust, &args.next()?.ok_or(
380 ParseError::new("lookup expects 2 arguments", &expression))?
381 )?;
382 rust.code.push(postfix);
383 Ok(())
384 }
385
386 fn resolve_helper(&self, expression: &Expression<'a>, name: Token<'a>, mut args: Token<'a>, rust: &mut Rust) -> Result<()>{
388 match name.value{
389 "lookup" => self.resolve_lookup(expression, "[", ']', args, rust),
390 "try_lookup" => self.resolve_lookup(expression, ".get(", ')', args, rust),
391 name => {
392 rust.code.push_str(name);
393 rust.code.push('(');
394 self.write_var(expression, rust, &args)?;
395 loop {
396 args = match args.next()?{
397 Some(token) => {
398 rust.code.push_str(", ");
399 self.write_var(expression, rust, &token)?;
400 token
401 },
402 None => {
403 rust.code.push(')');
404 return Ok(());
405 }
406 };
407 }
408 }
409 }
410 }
411
412 fn resolve(&self, expression: &Expression<'a>, rust: &mut Rust) -> Result<()>{
414 let token = match Token::first(&expression.content)?{
415 Some(token) => token,
416 None => return Err(ParseError::new("expected token", &expression))
417 };
418 rust.code.push_str(expression.prefix);
419 if let TokenType::SubExpression(raw) = token.token_type{
420 self.resolve_sub_expression(raw, token.value, rust)?;
421 }
422 else if let Some(args) = token.next()?{
423 self.resolve_helper(expression, token, args, rust)?;
424 }
425 else{
426 self.write_var(expression, rust, &token)?;
427 }
428 rust.code.push_str(expression.postfix);
429 Ok(())
430 }
431
432 pub fn write_local(&self, rust: &mut String, local: &Local){
434 append_with_depth(self.open_stack.len(), match local{
435 Local::As(local) => local,
436 _ => "this"
437 }, rust);
438 }
439
440 fn close(&mut self, expression: Expression<'a>, rust: &mut Rust) -> Result<()>{
442 let scope = self.open_stack.pop().ok_or_else(|| ParseError::new("Mismatched block helper", &expression))?;
443 Ok(scope.opened.handle_close(rust))
444 }
445
446 fn open(&mut self, expression: Expression<'a>, rust: &mut Rust) -> Result<()>{
448 let token = Token::first(&expression.content)?.ok_or_else(|| ParseError::new("expected token", &expression))?;
449 match self.block_map.get(token.value){
450 Some(block) => {
451 self.open_stack.push(Scope{
452 opened: block.open(self, token, &expression, rust)?,
453 depth: self.open_stack.len()
454 });
455 Ok(())
456 },
457 None => Err(ParseError::new(&format!("unsupported block helper {}", token.value), &expression))
458 }
459 }
460}
461
462#[derive(Debug, Clone, Copy)]
464pub struct Options{
465 pub root_var_name: Option<&'static str>,
467 pub write_var_name: &'static str
469}
470
471pub struct Compiler{
473 clean: Regex,
475 options: Options,
477 block_map: BlockMap
479}
480
481impl Compiler {
482 pub fn new(options: Options, block_map: BlockMap) -> Self{
484 Self{
485 clean: Regex::new("[\\\\\"\\{\\}]").unwrap(),
486 options,
487 block_map
488 }
489 }
490
491 fn escape<'a>(&self, content: &'a str) -> Cow<'a, str> {
493 self.clean.replace_all(
494 &content, |captures: &Captures| match &captures[0]{
495 "{" | "}" => format!("{}{}", &captures[0], &captures[0]),
496 _ => format!("\\{}", &captures[0])
497 }
498 )
499 }
500
501 fn commit_pending<'a>(&self, pending: &mut Vec<PendingWrite<'a>>, compile: &mut Compile<'a>, rust: &mut Rust) -> Result<()>{
503 if pending.is_empty(){
504 return Ok(());
505 }
506 rust.code.push_str("write!(");
507 rust.code.push_str(self.options.write_var_name);
508 rust.code.push_str(", \"");
509 for pending in pending.iter(){
510 match pending{
511 PendingWrite::Raw(raw) => rust.code.push_str(self.escape(raw).as_ref()),
512 PendingWrite::Expression(_) => rust.code.push_str("{}"),
513 PendingWrite::Format((_, format, _)) => rust.code.push_str(format)
514 }
515 }
516 rust.code.push('"');
517 for pending in pending.iter(){
518 match pending{
519 PendingWrite::Expression((expression, uses, display)) => {
520 compile.resolve(&Expression{
521 expression_type: ExpressionType::Raw,
522 prefix: ", ",
523 content: expression.content,
524 postfix: display,
525 raw: expression.raw
526 }, rust)?;
527 rust.using.insert(uses.to_string());
528 },
529 PendingWrite::Format((raw, _, content)) => {
530 compile.resolve(&Expression{
531 expression_type: ExpressionType::Raw,
532 prefix: ", ",
533 content,
534 postfix: "",
535 raw
536 }, rust)?;
537 },
538 _ => ()
539 }
540 }
541 rust.code.push_str(")?;");
542 pending.clear();
543 Ok(())
544 }
545
546 fn select_write<'a>(expression: &Expression<'a>, uses: &'static str, postfix: &'static str) -> Result<PendingWrite<'a>>{
547 if let Some(token) = Token::first(&expression.content)?{
548 if let TokenType::Variable = token.token_type{
549 if token.value != "format"{
550 return Ok(PendingWrite::Expression((expression.clone(), uses, postfix)));
551 }
552 let pattern = match token.next()?{
553 Some(token) => token,
554 _ => return Ok(PendingWrite::Expression((expression.clone(), uses, postfix)))
555 };
556 let value = match pattern.next(){
557 Ok(Some(token)) => token,
558 _ => return Err(ParseError::new("format requires 2 arguments", expression))
559 };
560 if let TokenType::Literal = pattern.token_type{
561 if pattern.value.starts_with('"') && pattern.value.ends_with('"'){
562 return Ok(PendingWrite::Format((expression.raw, &pattern.value[1..pattern.value.len() - 1], value.value)));
563 }
564 }
565 return Err(ParseError::new("first argument of format must be a string literal", expression));
566 }
567 }
568 Ok(PendingWrite::Expression((expression.clone(), uses, postfix)))
569 }
570
571 pub fn compile(&self, src: &str) -> Result<Rust>{
573 let mut compile = Compile::new(self.options.root_var_name, &self.block_map);
574 let mut rust = Rust::new();
575 let mut pending: Vec<PendingWrite> = Vec::new();
576 let mut rest = src;
577 let mut expression = Expression::from(src)?;
578 while let Some(expr) = expression{
579 let Expression{
580 expression_type,
581 prefix,
582 content,
583 postfix,
584 raw: _
585 } = &expr;
586 rest = postfix;
587 if !prefix.is_empty(){
588 pending.push(PendingWrite::Raw(prefix));
589 }
590 match expression_type{
591 ExpressionType::Raw => pending.push(Self::select_write(&expr, USE_AS_DISPLAY, ".as_display()")?),
592 ExpressionType::HtmlEscaped => if *content == "else" {
593 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
594 compile.handle_else(&expr, &mut rust)?
595 } else {
596 pending.push(Self::select_write(&expr, USE_AS_DISPLAY_HTML, ".as_display_html()")?)
597 },
598 ExpressionType::Open => {
599 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
600 compile.open(expr, &mut rust)?
601 },
602 ExpressionType::Close => {
603 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
604 compile.close(expr, &mut rust)?
605 },
606 ExpressionType::Escaped => pending.push(PendingWrite::Raw(content)),
607 _ => ()
608 };
609 expression = expr.next()?;
610 }
611 if !rest.is_empty(){
612 pending.push(PendingWrite::Raw(rest));
613 }
614 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
615 Ok(rust)
616 }
617}