primate
A small DSL for cross-language constants. Write your shared values once, generate typed Rust, TypeScript, and Python — with documentation, enums, type aliases, and bounds-checked numeric literals carried through to each target.
constants/ ┌────────────────────────────┐
└── limits.prim ─────────► │ primate build │
└────────────────────────────┘
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
src/generated/constants.rs web/src/generated/limits.ts scripts/generated/limits.py
Why
If you ship a backend, a web client, and a script, you've already written
MAX_UPLOAD_SIZE = 100 * 1024 * 1024 three times — and at least one of
them is wrong. primate makes that one declaration produce one number per
target, in the form each language wants:
// constants/limits.prim
//! Application-wide limits.
/// Maximum upload size for a single request.
u64 MAX_UPLOAD_SIZE = 100MiB
/// Time after which an idle session is considered offline.
duration OFFLINE_THRESHOLD = 20min
/// Severity level. Integer-backed for fast filtering.
enum LogLevel: u8 {
Debug = 0,
Info = 1,
Warn = 2,
Error = 3,
}
LogLevel DEFAULT_LEVEL = Info
Generated Rust (pub mod per namespace, std::time::Duration,
#[repr(u8)] enums):
Generated TypeScript (one .ts per namespace + index.ts,
Temporal.Duration available, real ES enums):
// generated/constants/limits.ts
export enum LogLevel {
Debug = 0,
Info = 1,
Warn = 2,
Error = 3,
}
export const maxUploadSize = 104857600 as const;
export const offlineThreshold = 1200000 as const; // milliseconds
export const defaultLevel = LogLevel.Info as const;
Generated Python (package directory, IntEnum, timedelta):
# generated/constants/limits.py
= 0
= 1
= 2
= 3
: = 104857600
: =
: =
Highlights
- Unit-suffixed literals.
30s,100MiB,5%,1w— readable in source, normalized in generated code. - Bounds-checked.
u32 X = 5GiBis a compile-time error. - Cross-namespace
use. Files inconstants/auth/*.primcanuse logging::LogLevel; the right import is emitted in each target. - One canonical formatter.
primate fmt— no flags. - Editor support. A real LSP server (
primate lsp) ships with the binary: diagnostics, hover, go-to-definition, find-references, format on save, and contextual completion (enum variants, unit suffixes). Editor integrations for Zed and VS Code are ineditors/. - Plugin protocol. Add a target language by writing any executable that reads JSON on stdin and writes JSON on stdout — see docs/plugins.
Install
This puts a primate binary in ~/.cargo/bin. Verify:
Quick start
&&
You'll see one file per target appear under the configured paths.
Project status
v0.1 — usable, but evolving. The language is stable enough to depend
on, generators produce idiomatic output for the three built-ins, and the
LSP works in real editors. Things still on the roadmap: @deprecated
attributes, configurable formatter rules, plugin distribution. See
docs/src/roadmap.md.
Documentation
The full book is at docs/, browsable via mdBook:
&&
Top-level pages:
License
MIT — see LICENSE.