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("&", "&")
.replace("<", "<")
.replace(">", ">");
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")
}