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}
131
132pub struct Rust{
134 pub using: HashSet<String>,
136 pub code: String
138}
139
140pub static USE_AS_DISPLAY: &str = "AsDisplay";
142pub static USE_AS_DISPLAY_HTML: &str = "AsDisplayHtml";
144
145pub struct Uses<'a>{
147 uses: &'a HashSet<String>
148}
149
150impl<'a> Display for Uses<'a>{
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 match self.uses.len(){
153 0 => (),
154 1 => write!(f, "use rusty_handlebars::{}", self.uses.iter().next().unwrap())?,
155 _ => {
156 f.write_str("use rusty_handlebars::")?;
157 let mut glue = '{';
158 for use_ in self.uses{
159 f.write_char(glue)?;
160 f.write_str(use_)?;
161 glue = ',';
162 }
163 f.write_str("}")?;
164 }
165 }
166 Ok(())
167 }
168}
169
170impl Rust{
171 pub fn new() -> Self{
173 Self{
174 using: HashSet::new(),
175 code: String::new()
176 }
177 }
178
179 pub fn uses(&self) -> Uses{
181 Uses{ uses: &self.using}
182 }
183}
184
185pub trait Block{
187 fn handle_close<'a>(&self, rust: &mut Rust) {
189 rust.code.push_str("}");
190 }
191
192 fn resolve_private<'a>(&self, _depth: usize, expression: &'a Expression<'a>, _name: &str, _rust: &mut Rust) -> Result<()>{
194 Err(ParseError::new(&format!("{} not expected ", expression.content), expression))
195 }
196
197 fn handle_else<'a>(&self, expression: &'a Expression<'a>, _rust: &mut Rust) -> Result<()>{
199 Err(ParseError::new("else not expected here", expression))
200 }
201
202 fn this<'a>(&self) -> Option<&str>{
204 None
205 }
206
207 fn local<'a>(&self) -> &Local{
209 &Local::None
210 }
211}
212
213pub trait BlockFactory{
215 fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>>;
217}
218
219pub type BlockMap = HashMap<&'static str, &'static dyn BlockFactory>;
221
222pub struct Compile<'a>{
224 pub open_stack: Vec<Scope>,
226 pub block_map: &'a BlockMap
228}
229
230pub fn append_with_depth(depth: usize, var: &str, buffer: &mut String){
232 buffer.push_str(var);
233 buffer.push('_');
234 buffer.push_str(depth.to_string().as_str());
235}
236
237struct Root<'a>{
239 this: Option<&'a str>
240}
241
242impl<'a> Block for Root<'a>{
243 fn this<'b>(&self) -> Option<&str>{
244 self.this
245 }
246}
247
248impl<'a> Compile<'a>{
249 fn new(this: Option<&'static str>, block_map: &'a BlockMap) -> Self{
251 Self{
252 open_stack: vec![Scope{
253 depth: 0,
254 opened: Box::new(Root{this})
255 }],
256 block_map
257 }
258 }
259
260 fn find_scope(&self, var: &'a str) -> Result<(&'a str, &Scope)>{
262 let mut scope = self.open_stack.last().unwrap();
263 let mut local = var;
264 loop {
265 if 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 continue;
272 }
273 }
274 }
275 return Ok((local, scope));
276 }
277 }
278
279 fn resolve_local(&self, depth: usize, var: &'a str, local: &'a str, buffer: &mut String) -> bool{
281 if var.starts_with(local){
282 let len = local.len();
283 if var.len() > len{
284 if &var[len .. len + 1] != "."{
285 return false;
286 }
287 append_with_depth(depth, local, buffer);
288 buffer.push_str(&var[len ..]);
289 }
290 else{
291 append_with_depth(depth, local, buffer);
292 }
293 return true;
294 }
295 return false;
296 }
297
298 fn resolve_var(&self, var: &'a str, scope: &Scope, buffer: &mut String) -> Result<()>{
300 if scope.depth == 0{
301 if let Some(this) = scope.opened.this(){
302 buffer.push_str(this);
303 buffer.push('.');
304 }
305 buffer.push_str(var);
306 return Ok(());
307 }
308 if match scope.opened.local(){
309 Local::As(local) => self.resolve_local(scope.depth, var, local, buffer),
310 Local::This => {
311 buffer.push_str("this_");
312 buffer.push_str(scope.depth.to_string().as_str());
313 if var != "this"{
314 buffer.push('.');
315 buffer.push_str(var);
316 }
317 true
318 },
319 Local::None => false
320 }{
321 return Ok(());
322 }
323 let parent = &self.open_stack[scope.depth - 1];
324 if let Some(this) = scope.opened.this(){
325 self.resolve_var(this, parent, buffer)?;
326 if var != this{
327 buffer.push('.');
328 buffer.push_str(var);
329 }
330 }
331 else{
332 self.resolve_var(var, parent, buffer)?;
333 }
334 return Ok(());
335 }
336
337 fn resolve_sub_expression(&self, raw: &str, value: &str, rust: &mut Rust) -> Result<()>{
339 self.resolve(&Expression {
340 expression_type: ExpressionType::Raw,
341 prefix: "",
342 content: value,
343 postfix: "",
344 raw
345 }, rust)
346 }
347
348 pub fn write_var(&self, expression: &Expression<'a>, rust: &mut Rust, var: &Token<'a>) -> Result<()>{
350 match var.token_type{
351 TokenType::PrivateVariable => {
352 let (name, scope) = self.find_scope(var.value)?;
353 scope.opened.resolve_private(scope.depth, expression, name, rust)?;
354 },
355 TokenType::Literal => {
356 let (name, scope) = self.find_scope(var.value)?;
357 self.resolve_var(name, scope, &mut rust.code)?;
358 },
359 TokenType::SubExpression(raw) => {
360 self.resolve_sub_expression(raw, var.value, rust)?;
361 }
362 }
363 Ok(())
364 }
365
366 fn handle_else(&self, expression: &Expression<'a>, rust: &mut Rust) -> Result<()>{
368 match self.open_stack.last() {
369 Some(scope) => scope.opened.handle_else(expression, rust),
370 None => Err(ParseError::new("else not expected here", expression))
371 }
372 }
373
374 fn resolve_lookup(&self, expression: &Expression<'a>, prefix: &str, postfix: char, args: Token<'a>, rust: &mut Rust) -> Result<()>{
376 self.write_var(expression, rust, &args)?;
377 rust.code.push_str(prefix);
378 self.write_var(expression, rust, &args.next()?.ok_or(
379 ParseError::new("lookup expects 2 arguments", &expression))?
380 )?;
381 rust.code.push(postfix);
382 Ok(())
383 }
384
385 fn resolve_helper(&self, expression: &Expression<'a>, name: Token<'a>, mut args: Token<'a>, rust: &mut Rust) -> Result<()>{
387 match name.value{
388 "lookup" => self.resolve_lookup(expression, "[", ']', args, rust),
389 "try_lookup" => self.resolve_lookup(expression, ".get(", ')', args, rust),
390 name => {
391 rust.code.push_str(name);
392 rust.code.push('(');
393 self.write_var(expression, rust, &args)?;
394 loop {
395 args = match args.next()?{
396 Some(token) => {
397 rust.code.push_str(", ");
398 self.write_var(expression, rust, &token)?;
399 token
400 },
401 None => {
402 rust.code.push(')');
403 return Ok(());
404 }
405 };
406 }
407 }
408 }
409 }
410
411 fn resolve(&self, expression: &Expression<'a>, rust: &mut Rust) -> Result<()>{
413 let token = match Token::first(&expression.content)?{
414 Some(token) => token,
415 None => return Err(ParseError::new("expected token", &expression))
416 };
417 rust.code.push_str(expression.prefix);
418 if let TokenType::SubExpression(raw) = token.token_type{
419 self.resolve_sub_expression(raw, token.value, rust)?;
420 }
421 else if let Some(args) = token.next()?{
422 self.resolve_helper(expression, token, args, rust)?;
423 }
424 else{
425 self.write_var(expression, rust, &token)?;
426 }
427 rust.code.push_str(expression.postfix);
428 Ok(())
429 }
430
431 pub fn write_local(&self, rust: &mut String, local: &Local){
433 append_with_depth(self.open_stack.len(), match local{
434 Local::As(local) => local,
435 _ => "this"
436 }, rust);
437 }
438
439 fn close(&mut self, expression: Expression<'a>, rust: &mut Rust) -> Result<()>{
441 let scope = self.open_stack.pop().ok_or_else(|| ParseError::new("Mismatched block helper", &expression))?;
442 Ok(scope.opened.handle_close(rust))
443 }
444
445 fn open(&mut self, expression: Expression<'a>, rust: &mut Rust) -> Result<()>{
447 let token = Token::first(&expression.content)?.ok_or_else(|| ParseError::new("expected token", &expression))?;
448 match self.block_map.get(token.value){
449 Some(block) => {
450 self.open_stack.push(Scope{
451 opened: block.open(self, token, &expression, rust)?,
452 depth: self.open_stack.len()
453 });
454 Ok(())
455 },
456 None => Err(ParseError::new(&format!("unsupported block helper {}", token.value), &expression))
457 }
458 }
459}
460
461#[derive(Debug, Clone, Copy)]
463pub struct Options{
464 pub root_var_name: Option<&'static str>,
466 pub write_var_name: &'static str
468}
469
470pub struct Compiler{
472 clean: Regex,
474 options: Options,
476 block_map: BlockMap
478}
479
480impl Compiler {
481 pub fn new(options: Options, block_map: BlockMap) -> Self{
483 Self{
484 clean: Regex::new("[\\\\\"\\{\\}]").unwrap(),
485 options,
486 block_map
487 }
488 }
489
490 fn escape<'a>(&self, content: &'a str) -> Cow<'a, str> {
492 self.clean.replace_all(
493 &content, |captures: &Captures| match &captures[0]{
494 "{" | "}" => format!("{}{}", &captures[0], &captures[0]),
495 _ => format!("\\{}", &captures[0])
496 }
497 )
498 }
499
500 fn commit_pending<'a>(&self, pending: &mut Vec<PendingWrite<'a>>, compile: &mut Compile<'a>, rust: &mut Rust) -> Result<()>{
502 if pending.is_empty(){
503 return Ok(());
504 }
505 rust.code.push_str("write!(");
506 rust.code.push_str(self.options.write_var_name);
507 rust.code.push_str(", \"");
508 for pending in pending.iter(){
509 match pending{
510 PendingWrite::Raw(raw) => rust.code.push_str(self.escape(raw).as_ref()),
511 PendingWrite::Expression(_) => rust.code.push_str("{}")
512 }
513 }
514 rust.code.push('"');
515 for pending in pending.iter(){
516 if let PendingWrite::Expression((expression, uses, display)) = pending{
517 compile.resolve(&Expression{
518 expression_type: ExpressionType::Raw,
519 prefix: ", ",
520 content: expression.content,
521 postfix: display,
522 raw: expression.raw
523 }, rust)?;
524 rust.using.insert(uses.to_string());
525 }
526 }
527 rust.code.push_str(")?;");
528 pending.clear();
529 Ok(())
530 }
531
532 pub fn compile(&self, src: &str) -> Result<Rust>{
534 let mut compile = Compile::new(self.options.root_var_name, &self.block_map);
535 let mut rust = Rust::new();
536 let mut pending: Vec<PendingWrite> = Vec::new();
537 let mut rest = src;
538 let mut expression = Expression::from(src)?;
539 while let Some(expr) = expression{
540 let Expression{
541 expression_type,
542 prefix,
543 content,
544 postfix,
545 raw: _
546 } = &expr;
547 rest = postfix;
548 if !prefix.is_empty(){
549 pending.push(PendingWrite::Raw(prefix));
550 }
551 match expression_type{
552 ExpressionType::Raw => pending.push(PendingWrite::Expression((expr.clone(), USE_AS_DISPLAY, ".as_display()"))),
553 ExpressionType::HtmlEscaped => if *content == "else" {
554 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
555 compile.handle_else(&expr, &mut rust)?
556 } else {
557 pending.push(PendingWrite::Expression((expr.clone(), USE_AS_DISPLAY_HTML, ".as_display_html()")))
558 },
559 ExpressionType::Open => {
560 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
561 compile.open(expr, &mut rust)?
562 },
563 ExpressionType::Close => {
564 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
565 compile.close(expr, &mut rust)?
566 },
567 ExpressionType::Escaped => pending.push(PendingWrite::Raw(content)),
568 _ => ()
569 };
570 expression = expr.next()?;
571 }
572 if !rest.is_empty(){
573 pending.push(PendingWrite::Raw(rest));
574 }
575 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
576 Ok(rust)
577 }
578}