# metering โ task runner (https://just.systems)
#
# `just` with no arguments lists every recipe.
set shell := ["bash", "-uc"]
# MSRV โ keep in sync with `rust-version` in Cargo.toml and rust-toolchain.toml.
msrv := "1.94"
# ๐ List all recipes
default:
@just --list
# โ
Everything CI runs, in CI order
ci: fmt-check lint purity test example doc package
@echo "โ
all checks passed"
# ๐ง Enforce the "zero I/O, no clock" guarantee over non-comment source lines
purity:
#!/usr/bin/env bash
set -uo pipefail
hits="$(grep -rn --include='*.rs' -E \
'now_utc|SystemTime::now|Instant::now|std::(fs|env|net|process)|\bunsafe\b' \
src/ | grep -vE ':[[:space:]]*(///|//!|//)' || true)"
if [ -n "$hits" ]; then
echo "โ ambient state or unsafe reached the source:" >&2
echo "$hits" >&2
echo "" >&2
echo "This crate promises equal inputs give equal outputs. Take the" >&2
echo "timestamp as a parameter instead โ see the Determinism section." >&2
exit 1
fi
echo "๐ง pure: no clock, no I/O, no unsafe"
# ๐จ Format the workspace
fmt:
cargo fmt --all
# ๐จ Fail if anything is unformatted
fmt-check:
cargo fmt --all -- --check
# ๐ Clippy with warnings denied (default + all features)
lint:
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --all-features -- -D warnings
# ๐ Fast type-check, all features
check:
cargo check --all-targets --all-features
# ๐งช Full test suite (all features, incl. doctests)
test:
cargo test --all-features
# โถ๏ธ Run the end-to-end pipeline example
example:
cargo run --all-features --example pipeline
# ๐งช Run tests matching a filter, e.g. `just test-one gas_m3`
test-one filter:
cargo test --all-features {{ filter }} -- --nocapture
# ๐๏ธ Build every feature combination
features:
cargo build --no-default-features
cargo build --no-default-features --features serde
cargo build --all-features
# ๐ฆ Compile on the pinned MSRV
msrv:
RUSTUP_TOOLCHAIN={{ msrv }} cargo check --all-features --all-targets
# ๐ Build the docs with warnings denied
doc:
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
# ๐ Build and open the docs
doc-open:
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features --open
# ๐ฆ Dry-run the crates.io package (catches bad metadata before tagging)
# `--allow-dirty` is local-only convenience; CI runs this on a clean checkout.
package:
cargo publish --dry-run --all-features --allow-dirty
# ๐ก๏ธ Audit dependencies for advisories (needs `cargo install cargo-audit`)
audit:
cargo audit
# ๐ What would break for a consumer on the last published version
# (needs `cargo install cargo-semver-checks`). CI runs it as a report, not a
# gate, while the crate is pre-1.0; its output is the list a CHANGELOG entry
# needs.
#
# `--release-type patch` asks "would this be a legal patch release?", which
# is the only phrasing that lists every break. Left off, the tool infers the
# bump from Cargo.toml, and a 0.x minor bump already declares a major change
# โ so it skips every lint and reports a clean run over a hard cut.
semver:
cargo semver-checks check-release --all-features --release-type patch
# โฌ๏ธ Show outdated dependencies (needs `cargo install cargo-outdated`)
outdated:
cargo outdated --root-deps-only
# ๐ท๏ธ Tag the current Cargo.toml version and push it โ triggers release.yml
tag:
#!/usr/bin/env bash
set -euo pipefail
version="$(cargo metadata --no-deps --format-version 1 \
| grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4)"
if [ -n "$(git status --porcelain)" ]; then
echo "โ working tree is dirty โ commit first" >&2
exit 1
fi
just ci
git tag -a "v${version}" -m "v${version}"
echo "๐ท๏ธ tagged v${version} โ push with: git push origin v${version}"
# Verify every passage the crate presents as verbatim source text.
#
# Needs `specs/` (run `just specs`) and `pdftotext` (poppler). Extra corpora can
# be passed as arguments โ the sibling workspaces hold documents this crate
# cites but does not mirror.
#
# Not a CI lane: the corpus is gitignored, so a fresh checkout has nothing to
# check against. Run it every audit round, and after touching a quote.
#
# ๐ Check every German quote in src/, site/ and README.md against the PDFs
quotes *dirs:
python3 scripts/verify_quotes.py {{ dirs }}
# ๐ Serve the documentation site locally (needs `zola`)
site:
zola --root site serve
# ๐ Build the site and validate every internal link
site-build:
zola --root site check
zola --root site build --force
# ๐งน Remove build artifacts
clean:
cargo clean
rm -rf site/public
# Third-party publications: gitignored, never committed. Keeps whatever is
# already on disk, so a partial run is safe to repeat. What cannot be fetched
# is reported at the end and indexed in `specs/README.md` with its source.
#
# ๐ Rebuild `specs/` โ the primary sources every citation is checked against
specs:
#!/usr/bin/env bash
set -uo pipefail
ua='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36'
missing=""
# fetch DIR FILE URL [ALT_URL]
fetch() {
mkdir -p "specs/$1"
if [ -s "specs/$1/$2" ]; then echo "kept $1/$2"; return 0; fi
for url in "$3" "${4:-}"; do
[ -n "$url" ] || continue
if curl -fsSL -A "$ua" --retry 3 --retry-delay 5 --max-time 900 \
-o "specs/$1/$2.part" "$url" \
&& [ -s "specs/$1/$2.part" ] \
&& [ "$(file -b --mime-type "specs/$1/$2.part")" != "text/html" ]; then
mv "specs/$1/$2.part" "specs/$1/$2"; echo "fetched $1/$2"; return 0
fi
rm -f "specs/$1/$2.part"
done
echo "MISSING $1/$2 <- $3" >&2
missing="$missing $1/$2 <- $3"$'\n'
return 0
}
# law/ โ the statutes, consolidated (gesetze-im-internet.de)
fetch law enwg.pdf 'https://www.gesetze-im-internet.de/enwg_2005/EnWG.pdf'
fetch law msbg.pdf 'https://www.gesetze-im-internet.de/messbg/MsbG.pdf'
fetch law messeg.pdf 'https://www.gesetze-im-internet.de/messeg/MessEG.pdf'
fetch law messev.pdf 'https://www.gesetze-im-internet.de/messev/MessEV.pdf'
fetch law stromnev.pdf 'https://www.gesetze-im-internet.de/stromnev/StromNEV.pdf'
fetch law heizkostenv.pdf 'https://www.gesetze-im-internet.de/heizkostenv/HeizkostenV.pdf'
fetch law bgbl-2025-i-347-enwg-novelle-20251222.pdf \
'https://www.recht.bund.de/bgbl/1/2025/347/regelungstext.pdf?__blob=publicationFile&v=2'
# bnetza/ โ the Festlegungen the repealed ordinances left behind
fetch bnetza bk6-23-241-beschluss-20260507.pdf \
'https://www.bundesnetzagentur.de/DE/Beschlusskammern/1_GZ/BK6-GZ/2023/BK6-23-241/BK6-23-241_beschluss_vom_07.05.26.pdf?__blob=publicationFile&v=1'
fetch bnetza bk6-23-241-anlage-bilarem.pdf \
'https://www.bundesnetzagentur.de/DE/Beschlusskammern/1_GZ/BK6-GZ/2023/BK6-23-241/BK6-23-241_bilarem.pdf?__blob=publicationFile&v=1'
fetch bnetza bk7-24-01-008-gabi-gas-2-1-beschluss.pdf \
'https://www.bundesnetzagentur.de/DE/Beschlusskammern/1_GZ/BK7-GZ/2024/BK7-24-0008/Anlagen/BK7-24-01-0008_Beschluss_DL_BF.pdf?__blob=publicationFile&v=5'
fetch bnetza bk6-22-300-beschluss-20231127.pdf \
'https://www.bundesnetzagentur.de/DE/Beschlusskammern/1_GZ/BK6-GZ/2022/BK6-22-300/Beschluss/BK6-22-300_Beschluss_20231127.pdf?__blob=publicationFile&v=1'
fetch bnetza bk6-22-300-anlage1-20231127.pdf \
'https://www.bundesnetzagentur.de/DE/Beschlusskammern/1_GZ/BK6-GZ/2022/BK6-22-300/Beschluss/BK6-22-300_Beschluss_Anlage1.pdf?__blob=publicationFile&v=1'
fetch bnetza bk6-22-300-vde-fnn-empfehlung-tenorziffer-2f.pdf \
'https://www.bundesnetzagentur.de/DE/Beschlusskammern/1_GZ/BK6-GZ/2022/BK6-22-300/Mitteilung/Mitteilung_3/VDE_FNN_Empfehlung_zu_Tenorziffer_2f.pdf?__blob=publicationFile&v=1'
fetch bnetza bk6-24-174-beschluss-20241024.pdf \
'https://www.bundesnetzagentur.de/DE/Beschlusskammern/1_GZ/BK6-GZ/2024/BK6-24-174/Beschluss/BK6-24-174_Beschluss_vom_20241024.pdf?__blob=publicationFile&v=1'
fetch bnetza bk6-24-174-gpke-teil1-lesefassung.pdf \
'https://www.bundesnetzagentur.de/DE/Beschlusskammern/1_GZ/BK6-GZ/2024/BK6-24-174/Beschluss/BK6-24-174_GPKE_Teil1_Lesefassung.pdf?__blob=publicationFile&v=1'
# edi-energy/ โ the BDEW catalogue, one file per fileId
fetch edi-energy codeliste-obis-kennzahlen-und-medien-2.5c.pdf \
'https://www.bdew-mako.de/api/downloadFile/11918'
fetch edi-energy allgemeine-festlegungen-6.1c.pdf \
'https://www.bdew-mako.de/api/downloadFile/11916'
fetch edi-energy allgemeine-festlegungen-6.1d.pdf \
'https://www.bdew-mako.de/api/downloadFile/12145'
fetch edi-energy codeliste-slp-tu-muenchen-1.1.pdf \
'https://www.bdew-mako.de/api/downloadFile/9092'
fetch edi-energy mscons-mig-2.4c.pdf \
'https://www.bdew-mako.de/api/downloadFile/9645'
fetch edi-energy mscons-mig-2.5.pdf \
'https://www.bdew-mako.de/api/downloadFile/12175'
# bdew/ โ Anwendungshilfen and the gas-SLP Leitfaden
fetch bdew bdew-slp-strom-2025-profile-h25-g25-l25-p25-s25.xlsx \
'https://www.bdew.de/media/documents/Kopie_von_Repr%C3%A4sentative_Profile_BDEW_H25_G25_L25_P25_S25_Ver%C3%B6ffentlichung.xlsx'
fetch bdew bdew-lf-ausfallarbeit-redispatch-2-0-202005.pdf \
'https://www.bdew.de/media/documents/Awh_2020-05_RD_2.0_LF_Ausfallarbeit.pdf'
fetch bdew bdew-lf-slp-gas-kov-xv-20260327.pdf \
'https://www.bdew.de/media/documents/260327_LF_SLP_Gas_KoV_XV_CO4f7Rb.pdf'
fetch bdew bdew-lf-slp-gas-anlage2-pruefroutine-siglinde.xlsm \
'https://www.bdew.de/media/documents/20240322_KoV_XIV_LF-SLP_Anlage_2-Pr%C3%BCfroutine_Synthetisches_Verfahren_SigLinDe.xlsm'
fetch bdew bdew-awh-slp-strom-2025-20250317.pdf \
'https://www.bdew.de/media/documents/2025-03-17_AWH_Aktualisierte_SLP_Strom_2025_Ver%C3%B6ffentlichung.pdf'
fetch bdew bdew-awh-modul-3-v1.1-20250207.pdf \
'https://www.bdew.de/media/documents/BDEW-AWH_Modul_3_V1.1_Korrektur070225.pdf'
fetch bdew bdew-awh-identifikatoren-mako-v1.2.pdf \
'https://www.bdew.de/media/documents/AWH_Identifikatoren-in-der-Marktkommunikation_Version.1.2.pdf'
fetch bdew bdew-awh-malo-id-v1.0-20170428.pdf \
'https://bdew-codes.de/Content/Files/MaLo/2017-04-28-BDEW-Anwendungshilfe-MaLo-ID_Version1.0_FINAL.PDF' \
'https://www.bundesnetzagentur.de/DE/Beschlusskammern/_SharedDocs/Mitteilungen_zu_BK6_16_200_BK7_16_142_/Mitteilung_Nr_2/Anlage_1_Anwendungshilfe_MaLo_ID.pdf?__blob=publicationFile&v=1'
fetch bdew bdew-awh-eic-vergabe-v1.0-20171218.pdf \
'https://bdew-codes.de/Content/Files/EIC/Awh_20171218_EIC-Vergabe_V1-0.pdf'
# The EDI@Energy Anwendungshilfe carries the worked ยง 42b formulas; the BDEW
# Anwendungshilfe zum Solarpaket I is no longer served as a PDF.
fetch edi-energy awh-berechnungsformeln-solarpaket-1-v1.1.pdf \
'https://www.bdew-mako.de/api/downloadFile/11113'
# vde-fnn/ โ what the paywalled Anwendungsregeln are cited through
fetch vde-fnn vde-fnn-hinweis-bewertung-mindestleistung.pdf \
'https://www.vde.com/resource/blob/2384818/09cfe30a1ef5a210bebf37ff14953858/vde-fnn-hinweis-bewertung-der-mindestleistung-data.pdf'
fetch vde-fnn vde-fnn-hinweis-symmetrischer-anschluss-4100.pdf \
'https://www.vde.com/resource/blob/2243242/931a9d5c1e48cf5c55592bcda7c59e20/fnn-hinweis-anforderungen-fuer-den-symmetrischen-anschluss-und-betrieb-nach-vde-ar-n-4100-data.pdf'
# eu/ โ the Gastag's own definition
fetch eu vo-eu-312-2014-gasnetzkodex-bilanzierung-de.pdf \
'https://eur-lex.europa.eu/legal-content/DE/TXT/PDF/?uri=CELEX:32014R0312'
if [ -n "$missing" ]; then
echo "" >&2
echo "โ ๏ธ not fetched (see specs/README.md for the source):" >&2
printf '%s' "$missing" >&2
fi
echo "๐ specs/ rebuilt"