Welcome to Anaval!

I make things with computers. There could be some more things here but I don't know what to add.

This page is being generated by a single rust file1:

// All of the following code is licensed under CC0, WTFPL or the Unlicense at your option macro_rules! css { [ $( $selector:literal { $($rule:literal $(,)?)+ } )+ ] => { concat!( "\n", $( $selector, " {\n", $("\t", $rule, ";\n"),+ , "}\n" ),+ ) }; } const STYLE: &str = css![ "html" { "font-family: Liberation Sans" "font-size: 18px" "background: black" "color: white" } "body" { "margin: 0" } "h1" { "font-size: 2rem" } "header" { "padding: 1rem 0" "text-align: center" "background: hsl(30, 100%, 20%)" } "p" { "margin: 0" } ".high-text" { "text-align: center" "padding: 1rem" "margin: 1rem" "background-color: hsl(30, 100%, 10%)" } ".code" { "background: hsl(30, 100%, 10%)" "margin: 1rem" "padding: 0 1rem 1rem 1rem" } ".code p" { "padding-top: 1rem" } "code" { "background: hsl(30, 100%, 5%)" "padding-left: 0.5rem" "display: block" "tab-size: 4" "white-space: pre" } ]; fn main() { let viewport = [ "<meta", "name='viewport'", "content='width=device-width,initial-scale=1,shrink-to-fit=no'>", ] .join(" "); let site = html() .wrap( Tag::simple("head") .ln_in() .line("<meta charset=\"utf-8\">") .line(viewport) .ln() .wrap(Tag::simple("style").wrap(STYLE)), ) .wrap(body()); let out = std::path::PathBuf::from("out"); std::fs::write(out.join("index.html"), site.build()).unwrap(); } fn body() -> Tag { let code = std::fs::read_to_string("index.rs") .unwrap() .replace("&", "&amp;") .replace("<", "&lt;") .replace(">", "&gt;"); Tag::simple("body") .ln_in() .wrap( tag("header") .l() .wrap(Tag::simple("h1").wrap("Welcome to Anaval!")), ) .ln() .wrap( p().class("high-text").ln_in().wrap( [ "I make things with computers.", "There could be some more things here but I don't know what to add.", ] .join(" "), ), ) .ln() .wrap( div() .class("code") .wrap( p().ln_start() .wrap("This page is being generated by a single rust file") .wrap( tag("sup") .attr("title", "Yes, I use tabs for indenting.") .wrap("1"), ) .wrap(":"), ) .ln() .wrap(tag("code").wrap(code)) .ln(), ) } struct Tag(String, Content, String); impl Tag { fn simple(t: &str) -> Self { if ["head", "body"].contains(&t) { Self(format!("\n<{t}>\n"), Content::None, format!("\n</{t}>\n")) } else { Self(format!("<{t}>"), Content::None, format!("</{t}>")) } } #[expect(dead_code)] fn id(self, id: &str) -> Self { self.attr("id", id) } fn class(self, id: &str) -> Self { self.attr("class", id) } fn attr(mut self, attr: &str, v: &str) -> Self { let attr = &format!(" {attr}=\"{v}\""); self.0.insert_str(self.0.len() - 1, attr); self } fn line(mut self, c: impl ToString) -> Self { match self.1 { Content::Tags(_) => self.1 = Content::String(self.1.build() + &c.to_string() + "\n"), Content::String(ref mut s) => *s += &(c.to_string() + "\n"), Content::None => self.1 = Content::String(c.to_string() + "\n"), } self } fn wrap(mut self, c: impl Into<Content>) -> Self { match self.1 { Content::Tags(ref mut vec) => vec.extend(c.into().tags()), Content::String(ref mut s) => match c.into() { Content::Tags(t) => s.push_str(&t.into_iter().map(Tag::build).collect::<String>()), Content::String(c) if s.ends_with(char::is_whitespace) => s.push_str(&c), Content::String(c) => s.push_str(&c), Content::None => (), }, Content::None => self.1 = c.into(), } self } fn build(self) -> String { [self.0, self.1.build(), self.2].concat() } fn ln(self) -> Self { self.line("") } fn l(mut self) -> Self { self.0 = format!("{}\n", self.0); self.2 = format!("\n{}\n", self.2); self } fn ln_in(mut self) -> Self { self.0 = format!("{}\n", self.0); self.2 = format!("\n{}", self.2); self } fn ln_start(mut self) -> Self { self.0 = format!("\n{}", self.0); self } #[expect(dead_code)] fn ln_end(mut self) -> Self { self.2 = format!("{}\n", self.2); self } } enum Content { Tags(Vec<Tag>), String(String), None, } impl Content { fn build(self) -> String { match self { Content::Tags(vec) => vec.into_iter().map(Tag::build).collect(), Content::String(s) => s, Content::None => String::new(), } } fn tags(self) -> Vec<Tag> { if let Self::Tags(v) = self { v } else { panic!() } } } impl From<Tag> for Content { fn from(value: Tag) -> Self { Content::Tags(vec![value]) } } impl From<String> for Content { fn from(value: String) -> Self { Content::String(value) } } impl From<&str> for Content { fn from(value: &str) -> Self { Content::String(value.into()) } } fn tag(t: &str) -> Tag { if t == "html" { Tag( format!("<!doctype html>\n<{t}>"), Content::None, format!("</{t}>\n"), ) } else if ["head", "body"].contains(&t) { Tag(format!("\n<{t}>\n"), Content::None, format!("\n</{t}>\n")) } else { Tag(format!("<{t}>"), Content::None, format!("</{t}>")) } } fn p() -> Tag { tag("p") } fn div() -> Tag { tag("div") } fn html() -> Tag { tag("html") }