pamoja

About

Building#

Repository layout#

crates/      Rust engine and capability crates (each crate's README is its landing page)
bindings/    per-language bindings: node, python, dotnet
examples/    runnable end-to-end scenarios and the cross-language conformance generator
conformance/ the vectors every binding asserts, so the languages cannot disagree
docs/        this site: the guides, the capability map, and the pages about the project
sitl/        ArduPilot and PX4 SITL images for the MAVLink interop job
web/         the stylesheets, scripts, typefaces, and front-page data the site is rendered from
assets/      brand and logo

Device and transport simulators live in pamoja-sim and pamoja-loopback, so the examples and tests run with no hardware.

From source#

Shell
cargo build --workspace      # build the engine and capability crates
cargo test --workspace       # run tests, including doctests and the MQTT round-trip

cd bindings/node
npm install && npm run build  # build the native addon and the TypeScript facade
npm test                      # smoke and conformance tests

cd ../python
python -m venv .venv && . .venv/bin/activate
pip install maturin pytest
maturin develop -m packages/native/Cargo.toml                          # build the engine, pamoja-native
pip install $(find packages -mindepth 1 -maxdepth 1 -type d ! -name native)  # every pure distribution
pytest                                                                  # smoke and conformance tests

cd ../..
cargo build -p pamoja-ffi --release                       # build the native C ABI and refresh pamoja.h
dotnet build bindings/dotnet/Pamoja.sln -c Release    # build the .NET interop and facade
dotnet run --project bindings/dotnet/tests/Pamoja.Smoke -c Release  # smoke and conformance tests

just lists the recipes CI runs, and cargo xtask lists the workspace tasks.

Generated files#

Several committed files are generated and checked in CI, so edit the source they come from and regenerate:

FileSourceRegenerate with
crates/*/README.mdeach crate's lib.rs rustdoccargo xtask docs
docs/SUMMARY.md and the tables in the READMEs and this sitedocs/capabilities.tomlcargo xtask docs
crates/pamoja-ffi/include/pamoja.hthe pamoja-ffi sourcecargo build -p pamoja-ffi
bindings/node/packages/native/index.js and index.d.tsthe Node binding sourcenpm run build in bindings/node
bindings/node/packages/*/package.json, tsconfig.json, and README.mddocs/capabilities.toml and each package's importscargo xtask docs
bindings/python/packages/native/python/pamoja/_native/__init__.pyithe Python binding sourcecargo run --bin stub_gen in bindings/python/packages/native
bindings/python/packages/*/pyproject.toml, README.md, and py.typeddocs/capabilities.toml and each portion's importscargo xtask docs
conformance/vectors.jsonthe Rust implementationcargo run -p pamoja-examples --example conformance_vectors

Guide examples#

Every code block in a guide is spliced from a test that ran in CI. A guide page holds a region such as

<!-- snippet: bindings/python/guides/modbus.py#example -->
<!-- end -->

and cargo xtask docs fills it with the lines between # ANCHOR: example and # ANCHOR_END: example in that file, dedented, under a link to the file. cargo xtask docs --check fails if the spliced text no longer matches the source. One file per guide and language:

LanguageFileRun with
Rustexamples/tests/guides/<name>.rs, one #[test], declared in main.rscargo test -p pamoja-examples --test guides
TypeScriptbindings/node/guides/<name>.ts, top-level statements with node:assert/strictnpm run test:guides in bindings/node
Pythonbindings/python/guides/<name>.py, a script with plain assertpytest in bindings/python
C#bindings/dotnet/samples/Pamoja.Guides/<Name>Guide.cs, a static Run() called from Program.csdotnet run --project bindings/dotnet/samples/Pamoja.Guides

just guides runs all four. No example needs a broker, a server, or hardware: where a capability is a network client, the example covers what is decidable without one, and the loopback transport carries the round trips. The TypeScript files import the @pamoja/<name> packages through the workspace links under node_modules, so they see each package the way a user does; build the facade first. The C# project has a plain Guides namespace rather than a Pamoja.* one, so its examples name types the way an application does.

This site#

The pages are rendered from docs/ by cargo xtask site, a static-site generator in the workspace task runner, and the four references are generated into the same tree: rustdoc for the crates, typedoc for the Node packages, pdoc for the Python packages, and DocFX for the .NET packages. The reference page for each language is the way into its generated tree, whose own root pages redirect to it. Every code block is highlighted when the site is rendered, every link is checked (references included, once they are in place), and the docs workflow does all of it on every pull request; the Pages workflow publishes the same tree under /docs. Locally, the generators run first so the pages can overwrite their roots:

Shell
cargo xtask docs                           # the generated regions, checked in
mkdir -p target/site/docs/reference
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --exclude xtask --exclude pamoja-examples
cp -r target/doc target/site/docs/reference/rust
cd bindings/node/docs && npm ci && npx typedoc     # target/site/docs/reference/node
cd bindings/python && pip install pdoc==16.0 && pdoc pamoja '!pamoja._native' -o ../../target/site/docs/reference/python --docformat restructuredtext
dotnet tool install -g docfx --version 2.78.5 && docfx bindings/dotnet/docs/docfx.json
cargo xtask site                           # the pages, into target/site
cargo xtask site --verify                  # every link resolves, the references included
node web/serve.mjs --root target/site      # serve it at localhost:8099 with live reload

Formatting and lints#

CI runs cargo fmt --all -- --check and cargo clippy --workspace --all-targets -- -D warnings, and the same two over the Node and Python binding crates, which sit outside the workspace. Run both before pushing; the just ci recipe runs everything the main CI job does.

Edit this page on GitHub