snix/users/sterni/blërg/blërg.bqn
sterni 8a9fb693e3 refactor(sterni/blërg): runExecline is dead, long live Run Execline
Remember //nix/escapeExecline? Well, this is worse (for some possible
meanings of the word). Instead of taking a list and escaping/rendering
it to an execline script, Execline reimplements part of execlineb(1):

It takes a (nested) list where any enclosed list signifies an execline
block and produces the corresponding execline argv form as described in
execline-block(7). This means the result of Execline can directly be
executed using •SH without the need for execlineb(1).

Consequently, execlineb(1)'s management of positional parameters and the
environment are not available. This is fine for the intended purpose of
Execline (glueing together shell commands efficiently without messing
around with the FFI in BQN for pipe(2) etc.).

Change-Id: Ief69b1bab919c16b6e39c3f5dc3db628766c5a8c
Reviewed-on: https://cl.tvl.fyi/c/depot/+/13180
Reviewed-by: sterni <sternenseemann@systemli.org>
Autosubmit: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
2025-02-26 22:04:17 +00:00

179 lines
5.5 KiB
BQN
Executable file
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.

#!/usr/bin/env BQN
# SPDX-FileCopyrightText: Copyright © 2024-2025 sterni
# SPDX-License-Identifier: GPL-3.0-only
#
# blërg is a reimplementation of mblog in BQN. BQN is used as a sort of bespoke
# scripting languages so we can rely on external tools for certain tasks (e.g.
# transforming HTML and parsing MIME messages). A list of dependencies is
# maintained in README.md.
# Utilities
MkDirP •file.CreateDir(¬•file.Exists)
AsciiDown - ('A'-'a')×('A''Z')
Slugify '-'(('A' 'z') ¬ "-_0123456789"(˜)<)¨ AsciiDown
DropPrefix {𝕩(/)𝕨˜𝕩}
StripLeft (¬ `=)/
StripRight StripLeft
_join {((𝕗))´𝕩;𝕨𝕗𝕩}
nl @+10
SplitChar (= (¯1˙)¨ +`=)
Lines nlSplitChar
ReadPosInt {(𝕨×+)´ '0'-˜𝕩} # ty leah2
ReadPosDec 10ReadPosInt
Chomp {nl¯1𝕩? ¯1𝕩; 𝕩}
Run {
𝕊 𝕩: 1 𝕊 𝕩;
doChomp 𝕊 cmd:
exitstdoutstderr •SH cmd
exitChompdoChomp¨ stdoutstderr
}
R {𝕊 exitstdoutstderr: stderr!0=exit stdout}Run
LR LinesR
# see execline-block(7)
Execline {
# Not a string nor any other list
𝕩: 0•Type𝕩? •Fmt 𝕩;
# List, but not a string (i.e. block)
𝕩: 2•Type𝕩? ""˜(' '¨ 𝕊)¨𝕩;
# String (i.e. arg)
𝕩
}¨
GetEnv {R "importas""env"𝕩"printf""%s""$env"}
RelPath •wdpath•file.At
SplitExt ( (` + ¯2×) ='.')
# 3p dependencies
j {
# Update README.md if dependency discovery changes
Parse •Import (GetEnv "BQN_LIBS") •file.At "json.bqn"
ObjGet (() ˜1)<
ObjGetPath ObjGet˜´
_objGetDef {𝕨 (() ((𝕗))(˜1)) <𝕩}
}
# (Apple) Mail Notes Backend
# TODO(sterni): avoid argv limit by chunking
Hdrs {LR "mhdr""-dh"(':' _join 𝕨)𝕩}
Dates {ReadPosDec¨ LR "mhdr""-Dh""Date"𝕩}
headerNames "X-Uniform-Type-Identifier""X-Universally-Unique-Identifier""Subject"
MailNotesBackend {𝕊 config:
mailDir RelPath config j.ObjGet "maildir"
Entries {𝕊:
ms LR "mlist"mailDir
th ms,headerNamesheaderNames Hdrs ms
dh Dates ms
ah (("com.apple.mail-note")˘/) th˘dh˘ms
{𝕊 ·uuidtitletimepath:
title time
id Slugify uuid
Render {R "execline-cd"𝕩"mshow""-x"path R "mn2html"path}
}˘ ah
}
}
# Git Backend
converters >
# TODO(sterni): avoid cat
"html", "cat",
"md", "lowdown""-T""html""--html-no-skiphtml""--html-no-escapehtml""--html-callout-mdn",
# TODO(sterni): use emacs
"org", "pandoc""-f""org""-t""html5",
# TODO(sterni): pipefail
PipelineCmd {Execline "pipeline"𝕨𝕩}
GitBackend {𝕊 config:
repo RelPath config j.ObjGet "repository"
path '/' '/' StripRight config "." j._ObjGetDef "path"
# We use zero separated fields when dealing with paths, so quoting is unnecessary
GitCmd {"git""-c""core.quotePath=false""-C"repo𝕩}
rev R GitCmd "rev-parse""HEAD"
# Use the author date of the latest commit on the file to establish the date
# of the file. The author date is easier to arbitrarily change and survives
# history rewrites. It could be interesting to ignore commits that touch
# multiple files (especially treewide ones).
PathDate {ReadPosDec R GitCmd "log""--date=unix""--pretty=tformat:%ad""-1"rev"--"𝕩}
Entries {𝕤
blobs 2@ SplitChar R GitCmd "ls-tree""-zr""--format=%(path)%x00%(objectname)"revpath
{𝕊 pb:
extlesspext SplitExt p
id Slugify path DropPrefix extlessp
# TODO(sterni): extract from file if possible
title •file.Name extlessp
time PathDate p
Render {𝕤
conv converters j.ObjGet ext
R (GitCmd "cat-file""blob"b) PipelineCmd conv
}
}˘blobs
}
}
backends >"mail-notes"mailNotesBackend, "git"gitBackend
# Rendering
RenderPage {
"<!doctype html>
<html lang=""en"">
<head>
<meta charset=""utf-8"">
<title>"𝕨"</title>
<body>
<h1>"𝕨"</h1>"𝕩
}
WriteEntry {outDir 𝕊 entry:
entryDir MkDirP outDir •file.At entry.id
(entryDir •file.At "index.html") •file.Chars entry.title RenderPage entry.Render entryDir
# TODO(sterni): urlencode
"<li><a href="""entry.id""">"entry.title"</a></li>"
}
# Main
configFileoutDir {
# Usage: blërg <config file> <out dir>
! 2=•args
# TODO(sterni): expand ~/
RelPath¨ •args
}
config {
raw j.Parse •FChars configFile
[bns,bcs] raw j.ObjGet "backends"
bcs bcs ˘{21"name"𝕩}¨ bns
bts j.ObjGet"type"¨ bcs
backends bcs {𝕏 𝕨}¨ backendsj.ObjGet¨ bts
title raw j.ObjGet "title"
}
entries (( •ns.Get"time"¨)) {𝕩.Entries @}¨ config.backends
"All entry IDs must be unique"!(=) •ns.Get"id"¨ entries
MkDirP outDir
entryIndex outDirWriteEntry¨ entries
(outDir •file.At "index.html") •file.Chars config.title RenderPage "<ul>"entryIndex"</ul>"