snix/users/Profpatsch/blog/default.nix
Profpatsch 81e39b51cd feat(users/Profpatsch/blog): Add projects section
A new section for my awesome website.

Change-Id: I6c624aa0bfaf82aff943431da7499bec1d842c67
Reviewed-on: https://cl.tvl.fyi/c/depot/+/3291
Tested-by: BuildkiteCI
Reviewed-by: Profpatsch <mail@profpatsch.de>
2021-11-13 00:57:28 +00:00

340 lines
10 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{ depot, pkgs, lib, ... }:
let
bins = depot.nix.getBins pkgs.lowdown [ "lowdown" ]
// depot.nix.getBins pkgs.cdb [ "cdbget" "cdbmake" "cdbdump" ]
// depot.nix.getBins pkgs.coreutils [ "mv" "cat" "printf" "test" ]
// depot.nix.getBins pkgs.s6-networking [ "s6-tcpserver" ]
// depot.nix.getBins pkgs.time [ "time" ]
;
# /
toplevel = [
{
route = [ "notes" ];
name = "Notes";
page = router;
}
{
route = [ "projects" ];
name = "Projects";
# page = projects;
}
];
# /notes/*
notes = [
{
route = [ "notes" "preventing-oom" ];
name = "Preventing out-of-memory (OOM) errors on Linux";
page = renderNote "preventing-oom" ./notes/preventing-oom.md;
}
{
route = [ "notes" "rust-string-conversions" ];
name = "Converting between different String types in Rust";
page = renderNote "rust-string-conversions" ./notes/rust-string-conversions.md;
}
];
projects = [
{
name = "lorri";
description = "<code>nix-shell</code> replacement for projects";
link = "https://github.com/nix-community/lorri";
}
{
name = "netencode";
description = "A human-readble nested data exchange format inspired by netstrings and bencode.";
link = depotCgitLink { relativePath = "users/Profpatsch/netencode/README.md"; };
}
{
name = "yarn2nix";
description = ''nix dependency generator for the <a href="https://yarnpkg.com/"><code>yarn</code> Javascript package manager</a>'';
link = "https://github.com/Profpatsch/yarn2nix";
}
];
# convert a note to html via lowdown
renderNote = name: note: depot.nix.runExecline "${name}.html" {} [
"importas" "out" "out"
bins.lowdown "-s" "-Thtml" "-o" "$out" note
];
# all notes with `route` converted to an absolute path
notesFullRoute = lib.pipe notes [
(map (x@{route, ...}: x // { route = mkRoute route; }))
];
# a cdb from route to a netencoded version of data for each route
router = lib.pipe notesFullRoute [
(map (x: {
name = x.route;
value = depot.users.Profpatsch.netencode.gen.dwim x;
}))
lib.listToAttrs
(cdbMake "notes-router")
];
# Create a link to the given source file/directory, given the relative path in the depot repo.
# Checks that the file exists at evaluation time.
depotCgitLink = {
# relative path from the depot root (without leading /).
relativePath
}:
assert
(lib.assertMsg
(builtins.pathExists (depot.path + "/" + relativePath))
"depotCgitLink: path /${relativePath} does not exist in depot");
"https://code.tvl.fyi/tree/${relativePath}";
# look up a route by path ($1)
router-lookup = depot.nix.writeExecline "router-lookup" { readNArgs = 1; } [
cdbLookup router "$1"
];
runExeclineStdout = name: args: cmd: depot.nix.runExecline name args ([
"importas" "-ui" "out" "out"
"redirfd" "-w" "1" "$out"
] ++ cmd);
notes-index-html =
let o = notesFullRoute;
in ''
<ul>
${scope o (o: ''
<li><a href="${str o.route}">${esc o.name}</a></li>
'')}
</ul>
'';
notes-index = runExeclineStdout "notes-index" {
stdin = depot.users.Profpatsch.netencode.gen.dwim notesFullRoute;
} [
"withstdinas" "-in" "TEMPLATE_DATA"
"pipeline" [
bins.printf ''
<ul>
{{#.}}
<li><a href="{{route}}">{{name}}</a></li>
{{/.}}
</ul>
''
]
depot.users.Profpatsch.netencode.netencode-mustache
];
# A simple mustache-inspired string interpolation combinator
# that takes an object and a template (a function from o to string)
# and returns a string.
scope = o: tpl:
if builtins.typeOf o == "list" then
lib.concatMapStringsSep "\n" tpl o
else if builtins.typeOf o == "set" then
tpl o
else throw "${lib.generators.toPretty {} o} not allowed in template";
# string-escape html (TODO)
str = s: s;
# html-escape (TODO)
esc = s: s;
html = s: s;
projects-index-html =
let o = projects;
in ''
<dl>
${scope o (o: ''
<dt><a href="${str o.link}">${esc o.name}</a></dt>
<dd>${html o.description}</dd>
'')}
</dl>
'';
projects-index = runExeclineStdout "projects-index" {
stdin = depot.users.Profpatsch.netencode.gen.dwim projects;
} [
"withstdinas" "-in" "TEMPLATE_DATA"
"pipeline" [
bins.printf ''
<dl>
{{#.}}
<dt><a href="{{link}}">{{name}}</a></dt>
<dd>{{{description}}}</dd>
{{/.}}
</dl>
''
]
depot.users.Profpatsch.netencode.netencode-mustache
];
arglibNetencode = val: depot.nix.writeExecline "arglib-netencode" { } [
"export" "ARGLIB_NETENCODE" (depot.users.Profpatsch.netencode.gen.dwim val)
"$@"
];
# A simple http server that serves the site. Yes, its horrible.
site-server = { port }: depot.nix.writeExecline "blog-server" {} [
(depot.users.Profpatsch.lib.runInEmptyEnv [ "PATH" ])
bins.s6-tcpserver "127.0.0.1" port
bins.time "--format=time: %es" "--"
runOr return400
"pipeline" [
(arglibNetencode {
what = "request";
})
depot.users.Profpatsch.read-http
]
depot.users.Profpatsch.netencode.record-splice-env
runOr return500
"importas" "-i" "path" "path"
"if" [ depot.tools.eprintf "GET \${path}\n" ]
runOr return404
"backtick" "-ni" "TEMPLATE_DATA" [
# TODO: factor this out of here, this is routing not serving
"ifelse" [ bins.test "$path" "=" "/notes" ]
[ "export" "content-type" "text/html"
"export" "serve-file" notes-index
depot.users.Profpatsch.netencode.env-splice-record
]
"ifelse" [ bins.test "$path" "=" "/projects" ]
[ "export" "content-type" "text/html"
"export" "serve-file" projects-index
depot.users.Profpatsch.netencode.env-splice-record
]
# TODO: ignore potential query arguments. See 404 message
"pipeline" [ router-lookup "$path" ]
depot.users.Profpatsch.netencode.record-splice-env
"importas" "-ui" "page" "page"
"export" "content-type" "text/html"
"export" "serve-file" "$page"
depot.users.Profpatsch.netencode.env-splice-record
]
runOr return500
"if" [
"pipeline" [ bins.printf ''
HTTP/1.1 200 OK
Content-Type: {{{content-type}}}; charset=UTF-8
Connection: close
'' ]
depot.users.Profpatsch.netencode.netencode-mustache
]
"pipeline" [ "importas" "t" "TEMPLATE_DATA" bins.printf "%s" "$t" ]
depot.users.Profpatsch.netencode.record-splice-env
"importas" "-ui" "serve-file" "serve-file"
bins.cat "$serve-file"
];
# run argv or $1 if argv returns a failure status code.
runOr = depot.nix.writeExecline "run-or" { readNArgs = 1; } [
"foreground" [ "$@" ]
"importas" "?" "?"
"ifelse" [ bins.test "$?" "-eq" "0" ]
[]
"if" [ depot.tools.eprintf "runOr: exited \${?}, running \${1}\n" ]
"$1"
];
return400 = depot.nix.writeExecline "return400" {} [
bins.printf "%s" ''
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=UTF-8
Connection: close
''
];
return404 = depot.nix.writeExecline "return404" {} [
bins.printf "%s" ''
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=UTF-8
Connection: close
This page doesnt exist! Query arguments are not handled at the moment.
''
];
return500 = depot.nix.writeExecline "return500" {} [
bins.printf "%s" ''
HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=UTF-8
Connection: close
Encountered an internal server error. Please try again.
''
];
capture-stdin = depot.nix.writers.rustSimple {
name = "capture-stdin";
dependencies = [ depot.users.Profpatsch.execline.exec-helpers ];
} ''
extern crate exec_helpers;
use std::io::Read;
fn main() {
let (args, prog) = exec_helpers::args_for_exec("capture-stdin", 1);
let valname = &args[1];
let mut v : Vec<u8> = vec![];
std::io::stdin().lock().read_to_end(&mut v).unwrap();
exec_helpers::exec_into_args("capture-stdin", prog, vec![(valname, v)]);
}
'';
# go from a list of path elements to an absolute route string
mkRoute = route: "/" + lib.concatMapStringsSep "/" urlencodeAscii route;
# urlencodes, but only ASCII characters
# https://en.wikipedia.org/wiki/Percent-encoding
urlencodeAscii = urlPiece:
let
raw = [ "!" "#" "$" "%" "&" "'" "(" ")" "*" "+" "," "/" ":" ";" "=" "?" "@" "[" "]" ];
enc = [ "%21" "%23" "%24" "%25" "%26" "%27" "%28" "%29" "%2A" "%2B" "%2C" "%2F" "%3A" "%3B" "%3D" "%3F" "%40" "%5B" "%5D" ];
rest = [ "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "-" "_" "." "~" ];
in
assert lib.assertMsg (lib.all (c: builtins.elem c (raw ++ rest)) (lib.stringToCharacters urlPiece))
"urlencodeAscii: the urlPiece must only contain valid url ASCII characters, was: ${urlPiece}";
builtins.replaceStrings raw enc urlPiece;
# create a cdb record entry, as required by the cdbmake tool
cdbRecord = key: val:
"+${toString (builtins.stringLength key)},${toString (builtins.stringLength val)}:"
+ "${key}->${val}\n";
# create a full cdbmake input from an attribute set of keys to values (strings)
cdbRecords =
with depot.nix.yants;
defun [ (attrs (either drv string)) string ]
(attrs:
(lib.concatStrings (lib.mapAttrsToList cdbRecord attrs)) + "\n");
# run cdbmake on a list of key/value pairs (strings
cdbMake = name: attrs: depot.nix.runExecline "${name}.cdb" {
stdin = cdbRecords attrs;
} [
"importas" "out" "out"
depot.users.Profpatsch.lib.eprint-stdin
"if" [ bins.cdbmake "db" "tmp" ]
bins.mv "db" "$out"
];
# look up a key ($2) in the given cdb ($1)
cdbLookup = depot.nix.writeExecline "cdb-lookup" { readNArgs = 2; } [
# cdb ($1) on stdin
"redirfd" "-r" "0" "$1"
# key ($2) lookup
bins.cdbget "$2"
];
in depot.nix.utils.drvTargets {
inherit
router
depotCgitLink
site-server
notes-index
notes-index-html
projects-index
projects-index-html
router-lookup
;
}