{ 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; } ]; # /notes/* notes = [ { route = [ "notes" "preventing-oom" ]; name = "Preventing OOM"; 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; } ]; # 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") ]; # 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); index = runExeclineStdout "index" { stdin = depot.users.Profpatsch.netencode.gen.dwim notesFullRoute; } [ "withstdinas" "-in" "TEMPLATE_DATA" "pipeline" [ bins.printf '' '' ] 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, it’s horrible. notes-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" [ "ifelse" [ bins.test "$path" "=" "/notes" ] [ "export" "content-type" "text/html" "export" "serve-file" 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 doesn’t 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 = 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 notes-server index router-lookup ; }