rshtml_core 0.6.1

RsHtml: A Template Engine for Seamless HTML and Rust Integration.
Documentation
use super::Input;
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse_str;
use winnow::{
    ModalResult, Parser,
    combinator::{alt, cut_err, not, repeat},
    error::{AddContext, ContextError, ErrMode, StrContext, StrContextValue},
    stream::Stream,
    token::{any, none_of, take_while},
};

pub fn rust_block<'a, 'ctx>(input: &mut Input<'a, 'ctx>) -> ModalResult<TokenStream> {
    let start = input.input;
    let checkpoint = input.checkpoint();

    block.parse_next(input)?;

    let len = start.len() - input.len();
    let rust_block = &start[..len];

    let output = match parse_str::<syn::Block>(rust_block) {
        Ok(block) => {
            let stmts = &block.stmts;
            quote! { #(#stmts)* }
        }
        Err(e) => {
            let span = e.span();
            let start = span.start();

            let offset = rust_block
                .split_inclusive('\n')
                .take(start.line - 1)
                .map(|line| line.len())
                .sum::<usize>()
                + start.column;
            input.reset(&checkpoint);

            let _ = input.next_slice(offset);

            let error_msg = Box::leak(e.to_string().into_boxed_str());

            return Err(ErrMode::Cut(ContextError::new().add_context(
                input,
                &checkpoint,
                StrContext::Expected(StrContextValue::Description(error_msg)),
            )));
        }
    };

    Ok(output)
}

fn block<'a, 'ctx>(input: &mut Input<'a, 'ctx>) -> ModalResult<()> {
    (
        "{",
        block_content,
        cut_err("}").context(StrContext::Expected(StrContextValue::CharLiteral('}'))),
    )
        .void()
        .parse_next(input)
}

fn block_content<'a, 'ctx>(input: &mut Input<'a, 'ctx>) -> ModalResult<()> {
    repeat(
        0..,
        alt((
            block.void(),
            line_comment,
            block_comment,
            string_literal,
            char_literal,
            take_while(1.., |c| !r#"{}"'/"#.contains(c)).void(),
            none_of('}').void(),
        )),
    )
    .parse_next(input)
}

fn line_comment<'a, 'ctx>(input: &mut Input<'a, 'ctx>) -> ModalResult<()> {
    ("//", take_while(0.., |c| c != '\n'))
        .void()
        .parse_next(input)
}

fn block_comment<'a, 'ctx>(input: &mut Input<'a, 'ctx>) -> ModalResult<()> {
    (
        "/*",
        repeat(
            0..,
            alt((
                block_comment,
                take_while(1.., |c| c != '/' && c != '*').void(),
                (not(alt(("/*", "*/"))), any).void(),
            )),
        )
        .fold(|| (), |_, _| ()),
        "*/",
    )
        .void()
        .parse_next(input)
}

fn string_literal<'a, 'ctx>(input: &mut Input<'a, 'ctx>) -> ModalResult<()> {
    (
        '"',
        repeat(0.., alt((("\\", any).void(), none_of(['"', '\\']).void()))).map(|_: Vec<()>| ()),
        '"',
    )
        .void()
        .parse_next(input)
}

fn char_literal<'a, 'ctx>(input: &mut Input<'a, 'ctx>) -> ModalResult<()> {
    (
        '\'',
        alt((("\\", any).void(), none_of(['"', '\\']).void())),
        '\'',
    )
        .void()
        .parse_next(input)
}