New Document Templating Approach

I’ve just released the first version of alethea library which aims to define compile time templates ( Html, …) allowing rust code inside the templates

You may be interested in functionality of std::write macro if you haven't had opportunity to see it:

use std::fmt::Write;

struct AnimalsHtmlInput { animals: Vec<Animal> }

fn animals_html(AnimalsHtmlInput {animals}: AnimalsHtmlInput) -> String {
    let mut r = String::new();
    write!(&mut r, 
r#"
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Pet Cards</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="bg-gray-100 p-10">
        <h1 class="text-3xl font-bold text-center mb-10">Our Pets</h1>
        <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
"#
    );

    for animal in animals.iter() {
        write!(
            &mut r,
r#"
            <!-- Card -->
            <div class="bg-white rounded-xl shadow-md overflow-hidden hover:shadow-xl transition">
                <img class="w-full h-40 object-cover" src="{}" alt="Animal">
                <div class="p-4">
                    <h2 class="text-lg font-semibold">{}</h2>
                    <p class="text-gray-500 text-sm mb-2">{}</p>
                    <p class="text-gray-600 text-sm mb-4">{}</p>
                    <button class="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600">
                        Adopt
                    </button>
                </div>
            </div>
"#,
            animal.image_url, animal.species, animal.description, animal.name
        );
    }

    write!(&mut r,
r#"
        </div>
    </body>
</html>
"#
    );
    r
}

/*
    Usage example :
    
    let animal_html = animals_html!(AnimalsHtmlInput{animals: animals});

    // Write the string directly to the file, panic on error
    fs::write(
        "examples/htmlgen_basic/output_html/animals.html",
        &animal_html,
    )
    .unwrap();

*/

yes this is from what i begin, and this is why the alethea is here.

  • Firstly i saw the current libraries … and i said why to learn another new syntax, and export functions in case of need inside template and have more efforts just to construct a string ….

  • So i decided to use directly the std:::string you are mentioning

  • and after that i started with putting all templates as functions like you mentioned in templates module, but i had the problem and the point to guarantee that the inputs are not mutated inside the template, so i shall make very attention in functions to definition to use a non mut pointer on some struct params, or simply implement the template inside and fn closure and then call it immediately …

  • And then to define the struct with necessary things was not so easy for many templates especially when we shall handle lifetimes …. , and in the user side he shall init this struct also before calling template function, which add unnecessary things

  • So as the templates are usually used for some specific endpoint in one place in the code, i decided to simplify it and instead of a function , i made a macro with macro rules for each template and then wrap the body inside a fn closure to guarantee immutability, and then inside the body use the std::string like you are mentioning, and the benefit of this is that user will not construct any struct he will just call the macro, and also as template param are not typed, he can use this in many places if he want with different types that fullfill the interfaces like if he is using only $var_param.name inside the template he can use this template in someway for all his structs that have ‘a’ member , instead of defining a generic function and complexify more the readability

  • And after that as i had many templates to handle the macro rules definition with params with std::string ( especially when having nested local mini template … ) and fn closure yo guarantee the immutability become a redundant things for me and unnecessary that makes the template not redeable and complex for someone who just want to define a template or someone who wants to read .

  • So to resolve this limitation and focus only on template, instead of having std::strings methods/ macros , closures , macro rules , or functions definitions with structs with lifetimes with generics sometimes in many places of, i decided to implement a two simple proc macros that will solve those , the new_template macro and append_template macro and by using this macros you have not to add any new structs …. Or define template function/ macros by your self, or pay attention to function param to be sure that this is no mut ref, or adding part like fn closure to guarantee immutability …

  • All the things and features like guarantee of immutablity, abstract concatenation so template will be more readable , have params with no types so we can use them for many types if it fulfills the interfaces used inside the template , also make inheritance more readble/easy to someone who want implement or just read the template …. All of this are already resolved by the alethea approach with those two macros