This commit is contained in:
liquidex 2024-03-08 17:06:47 +01:00
parent ebb4543f8d
commit b6e803cfee
11 changed files with 162 additions and 30 deletions

View file

@ -8,21 +8,30 @@
content.link = "programming/projects" content.link = "programming/projects"
+ ### projects + ### projects
% content.link = "programming/cxx"
id = "programming/cxx"
+ ### C++
% content.link = "programming/unreal-engine"
id = "programming/unreal-engine"
+ ### Unreal Engine
% content.link = "programming/opinions"
id = "programming/opinions"
+ ### opinions
% id = "01HPD4XQQ5GPQ20C6BPA8G670F" % id = "01HPD4XQQ5GPQ20C6BPA8G670F"
- ### blog - ### blog
% content.link = "programming/blog/tairu" % content.link = "programming/blog/tairu"
id = "01HPD4XQQ5WM0APCAX014HM43V" id = "01HPD4XQQ5WM0APCAX014HM43V"
+ tairu - an interactive exploration of 2D autotiling techniques + tairu - an interactive exploration of 2D autotiling techniques
- ### languages
% content.link = "programming/languages/cxx"
id = "programming/languages/cxx"
+ ### C++
% content.link = "programming/languages/lua"
id = "programming/languages/lua"
+ ### Lua
- ### technologies
% content.link = "programming/technologies/unreal-engine"
id = "programming/technologies/unreal-engine"
+ ### Unreal Engine
% content.link = "programming/opinions"
id = "programming/opinions"
+ ### opinions

View file

@ -0,0 +1,74 @@
- TODO: this page could really use an interactive Lua interpreter. can we have that?
- Lua is a really cool language! did you know that?
+ lots of people complain about it being really weird for various reasons, but these are generally superficial
- usually it's cosmetic stuff, so these aren't any arguments of technical merit, but...
- stuff like indexing from 1 instead of 0, which is _just a design choice_ and does not impact your programming that much
- in fact, one could argue that regular programmers are weird for counting from zero :thinking:
- or using `~=` instead of `!=`, which is _just a syntax choice_, and you only have to get used to it once
- or using `do`..`end` style blocks instead of `{`..`}`, which again is _just a syntax choice_ and does not impact programming that much
- it's a tad bit more line noise, but not that terrible. I [did design a language using `do`..`end` blocks][def:mica/repo] and it really doesn't look that bad
- but I think Lua is a pretty damn genius programming language.
- the use of tables as The One Data Structure for Literally Everything strikes me as a 200 IQ choice I could never come up with myself
- partly because it's so fucking bold I can literally not imagine myself designing a language with a strong distinction between hash tables and arrays, and even tuples and records!
but the authors of had the restraint to just have One. and that One is **tables.**
- tables are extremely powerful in what they can do, because they're more than just a way of structuring data - they also allow for interfacing with the language *syntax* through operator overloading
+ in fact object oriented programming in Lua is typically done by overloading the `[]` indexing operator.
- the way it works is that `a.b` is just syntax sugar for `a["b"]`, which means you overload `[]` to _fall back to another table_ - and that way you can achieve [prototype-based inheritance](https://en.wikipedia.org/wiki/Prototype-based_programming)!
```lua
local fallback = { b = 2 }
local base = { a = 1 }
-- The __index field can be both a function *and* a table.
-- { __index = the_table } is a shorthand for { __index = function (t, k) return the_table[k] end }
setmetatable(base, { __index = fallback })
assert(base.b == 2)
```
- TODO: even more on restraint (standard library)
- TODO: restrained sugar: function calls `require "abc"`, `do_stuff { with_a_table = 1 }`
- I really wish Lua had at least *a* form of static typing though, since knowing about errors you make early is _really_ helpful during development.
- it regularly happened to me that a type error I made only occured at *some point* later during runtime; and then you have to track down a reproduction case and make a fix at the source. not fun.
- and it's really a bummer that Lua is not that strict!
- global variables by default are a pretty bad design choice in my opinion. having any form of uncontrolled globals hurts local reasoning and makes it harder to tell whatever your code is going to do.
- but fortunately it is possible to freeze your global variables by setting a metatable on `_G`, which is a table that represents the global scope.
- TODO: example
- there are also some bits of syntax that haven't aged very well.
- as much as people complain about cosmetics [TODO: link], I think there's a particular design choice that has aged very poorly in the face of modern, functional programming - function literals.
these tend to be quite verbose in Lua which hurts readability in functional code:
```lua
local u = map(t, function (v) return v + 2 end)
```
compare that to JavaScript's arrow functions `=>`, which I think are a prime example of good syntax sugar that encourages more function-oriented programming:
```javascript
let u = t.map(v => v + 2)
```
- the lack of a pipelining operator `|>` is also an annoyance, albeit most modern imperative languages don't have it either.

View file

@ -7,13 +7,13 @@
% id = "01H8YGXP0ZWG6X3PB6GWSGKAT0" % id = "01H8YGXP0ZWG6X3PB6GWSGKAT0"
- both the fun and the good, and that which ruins my mood - both the fun and the good, and that which ruins my mood
% id = "programming/unreal-engine/blueprint" % id = "programming/technologies/unreal-engine/blueprint"
content.link = "programming/unreal-engine/blueprint" content.link = "programming/technologies/unreal-engine/blueprint"
+ ### thoughts on Blueprint + ### thoughts on Blueprint
% id = "01HP1FESY5WVJG4X80AZ4ZBX5D" % id = "01HP1FESY5WVJG4X80AZ4ZBX5D"
- ### random but cool things - ### random but cool things
% content.link = "programming/unreal-engine/fixes" % content.link = "programming/technologies/unreal-engine/fixes"
id = "01HP1FESY5ZS6YTZXA8QTT5V1Z" id = "01HP1FESY5ZS6YTZXA8QTT5V1Z"
+ data validation quick fixes + data validation quick fixes

View file

@ -343,7 +343,7 @@ impl Generator {
} }
} }
pub fn generate(paths: &Paths<'_>) -> anyhow::Result<Treehouse> { pub fn generate(paths: &Paths<'_>) -> anyhow::Result<(Config, Treehouse)> {
let start = Instant::now(); let start = Instant::now();
info!("loading config"); info!("loading config");
@ -386,13 +386,13 @@ pub fn generate(paths: &Paths<'_>) -> anyhow::Result<Treehouse> {
info!("generation done in {duration:?}"); info!("generation done in {duration:?}");
if !treehouse.has_errors() { if !treehouse.has_errors() {
Ok(treehouse) Ok((config, treehouse))
} else { } else {
bail!("generation errors occurred; diagnostics were emitted with detailed descriptions"); bail!("generation errors occurred; diagnostics were emitted with detailed descriptions");
} }
} }
pub fn regenerate_or_report_error(paths: &Paths<'_>) -> anyhow::Result<Treehouse> { pub fn regenerate_or_report_error(paths: &Paths<'_>) -> anyhow::Result<(Config, Treehouse)> {
info!("regenerating site content"); info!("regenerating site content");
let result = generate(paths); let result = generate(paths);

View file

@ -1,12 +1,15 @@
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
mod live_reload; mod live_reload;
use std::{borrow::Cow, net::Ipv4Addr, path::PathBuf, sync::Arc}; use std::{net::Ipv4Addr, path::PathBuf, sync::Arc};
use anyhow::Context; use anyhow::Context;
use axum::{ use axum::{
extract::{Path, RawQuery, State}, extract::{Path, RawQuery, State},
http::{header::CONTENT_TYPE, HeaderValue, StatusCode}, http::{
header::{CONTENT_TYPE, LOCATION},
HeaderValue, StatusCode,
},
response::{Html, IntoResponse, Response}, response::{Html, IntoResponse, Response},
routing::get, routing::get,
Router, Router,
@ -15,7 +18,10 @@ use log::{error, info};
use pulldown_cmark::escape::escape_html; use pulldown_cmark::escape::escape_html;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use crate::state::{Source, Treehouse}; use crate::{
config::Config,
state::{Source, Treehouse},
};
use super::Paths; use super::Paths;
@ -29,12 +35,18 @@ struct SystemPages {
} }
struct Server { struct Server {
config: Config,
treehouse: Treehouse, treehouse: Treehouse,
target_dir: PathBuf, target_dir: PathBuf,
system_pages: SystemPages, system_pages: SystemPages,
} }
pub async fn serve(treehouse: Treehouse, paths: &Paths<'_>, port: u16) -> anyhow::Result<()> { pub async fn serve(
config: Config,
treehouse: Treehouse,
paths: &Paths<'_>,
port: u16,
) -> anyhow::Result<()> {
let app = Router::new() let app = Router::new()
.route("/", get(index)) .route("/", get(index))
.route("/*page", get(page)) .route("/*page", get(page))
@ -44,6 +56,7 @@ pub async fn serve(treehouse: Treehouse, paths: &Paths<'_>, port: u16) -> anyhow
.route("/static/*file", get(static_file)) .route("/static/*file", get(static_file))
.fallback(get(four_oh_four)) .fallback(get(four_oh_four))
.with_state(Arc::new(Server { .with_state(Arc::new(Server {
config,
treehouse, treehouse,
target_dir: paths.target_dir.to_owned(), target_dir: paths.target_dir.to_owned(),
system_pages: SystemPages { system_pages: SystemPages {
@ -114,13 +127,17 @@ async fn static_file(Path(path): Path<String>, State(state): State<Arc<Server>>)
} }
async fn page(Path(path): Path<String>, State(state): State<Arc<Server>>) -> Response { async fn page(Path(path): Path<String>, State(state): State<Arc<Server>>) -> Response {
let path = if !path.ends_with(".html") { let bare_path = path.strip_suffix(".html").unwrap_or(&path);
Cow::Owned(path + ".html") if let Some(redirected_path) = state.config.redirects.page.get(bare_path) {
} else { return (
Cow::Borrowed(&path) StatusCode::MOVED_PERMANENTLY,
}; [(LOCATION, format!("{}/{redirected_path}", state.config.site))],
)
.into_response();
}
if let Ok(file) = tokio::fs::read(state.target_dir.join(&*path)).await { let html_path = format!("{bare_path}.html");
if let Ok(file) = tokio::fs::read(state.target_dir.join(&*html_path)).await {
([(CONTENT_TYPE, "text/html")], file).into_response() ([(CONTENT_TYPE, "text/html")], file).into_response()
} else { } else {
four_oh_four(State(state)).await four_oh_four(State(state)).await

View file

@ -17,6 +17,26 @@ pub struct Config {
/// Links exported to Markdown for use with reference syntax `[text][def:key]`. /// Links exported to Markdown for use with reference syntax `[text][def:key]`.
pub defs: HashMap<String, String>, pub defs: HashMap<String, String>,
/// Redirects for moving pages around. These are used solely by the treehouse server.
///
/// Note that redirects are only resolved _non-recursively_ by the server. For a configuration
/// like:
///
/// ```toml
/// page.redirects.foo = "bar"
/// page.redirects.bar = "baz"
/// ```
///
/// the user will be redirected from `foo` to `bar`, then from `bar` to `baz`. This isn't
/// optimal for UX and causes unnecessary latency. Therefore you should always make redirects
/// point to the newest version of the page.
///
/// ```toml
/// page.redirects.foo = "baz"
/// page.redirects.bar = "baz"
/// ```
pub redirects: Redirects,
/// Overrides for emoji filenames. Useful for setting up aliases. /// Overrides for emoji filenames. Useful for setting up aliases.
/// ///
/// On top of this, emojis are autodiscovered by walking the `static/emoji` directory. /// On top of this, emojis are autodiscovered by walking the `static/emoji` directory.
@ -30,6 +50,13 @@ pub struct Config {
pub pics: HashMap<String, String>, pub pics: HashMap<String, String>,
} }
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Redirects {
/// Page redirects. When a user navigates to a page, if they navigate to `url`, they will
/// be redirected to `page[url]`.
pub page: HashMap<String, String>,
}
impl Config { impl Config {
pub fn load(path: &Path) -> anyhow::Result<Self> { pub fn load(path: &Path) -> anyhow::Result<Self> {
let string = std::fs::read_to_string(path).context("cannot read config file")?; let string = std::fs::read_to_string(path).context("cannot read config file")?;

View file

@ -42,8 +42,8 @@ async fn fallible_main() -> anyhow::Result<()> {
generate: _, generate: _,
serve: serve_args, serve: serve_args,
} => { } => {
let treehouse = regenerate_or_report_error(&paths)?; let (config, treehouse) = regenerate_or_report_error(&paths)?;
serve(treehouse, &paths, serve_args.port).await?; serve(config, treehouse, &paths, serve_args.port).await?;
} }
Command::Fix(fix_args) => fix_file_cli(fix_args)?, Command::Fix(fix_args) => fix_file_cli(fix_args)?,

View file

@ -23,6 +23,7 @@ description = "a place on the Internet I like to call home"
"dispatchers/repo" = "https://github.com/liquidev/dispatchers" "dispatchers/repo" = "https://github.com/liquidev/dispatchers"
"abit/repo" = "https://github.com/abyteintime/abit" "abit/repo" = "https://github.com/abyteintime/abit"
"rokugo/repo" = "https://github.com/rokugo-lang/rokugo" "rokugo/repo" = "https://github.com/rokugo-lang/rokugo"
"mica/repo" = "https://github.com/mica-lang/mica"
# Blog posts I like to reference # Blog posts I like to reference
"article/function_coloring" = "https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/" "article/function_coloring" = "https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/"
@ -36,6 +37,10 @@ description = "a place on the Internet I like to call home"
"person/firstbober" = "https://firstbober.com" "person/firstbober" = "https://firstbober.com"
"person/vixenka" = "https://github.com/Vixenka" "person/vixenka" = "https://github.com/Vixenka"
[redirects.page]
"programming/cxx" = "programming/languages/cxx"
"programming/unreal-engine" = "programming/technologies/unreal-engine"
[emoji] [emoji]
[pics] [pics]