# syntax-workout-sdk
Workout tree algebra -- represent any physical workout as a recursive tree.
[](https://crates.io/crates/syntax-workout-core)
[](https://www.npmjs.com/package/@syntax-workout/sdk)
[](https://pypi.org/project/syntax-workout-sdk/)
[](https://github.com/nicetomeetyu/syntax-workout-sdk/actions/workflows/ci.yml)
[](LICENSE-MIT)
## Install
**Rust**
```toml
[dependencies]
syntax-workout-core = "0.1"
```
**Node.js**
```sh
npm install @syntax-workout/sdk
```
**Python**
```sh
pip install syntax-workout-sdk
```
## Core concept
Every workout is a tree of `Node`s. Leaf nodes are individual sets; inner nodes group them into exercises, blocks, sessions, days, weeks, phases, and programs.
```
Program
Week
Day
Session ("Upper Hypertrophy")
Block (Sequential) -- straight sets
Exercise ("Bench Press")
Set [100kg x 5]
Set [100kg x 5]
Block (Parallel) -- superset
Exercise ("DB Row")
Set [30kg x 10]
Exercise ("Lateral Raise")
Set [12.5kg x 15]
Block (Circuit {rounds: 3}) -- circuit
Exercise ("Squat")
Set [60kg x 15]
Exercise ("Push-up")
Set [20 reps]
Exercise ("Plank")
Set [60s]
```
Each `Node` carries a `kind`, a `payload`, optional `children`, and open-ended `metadata`.
## Quick example
**Rust** (builder API)
```rust
use syntax_workout_core::builder::*;
use syntax_workout_core::{ExecutionMode, Intensity};
use syntax_workout_core::measure::WeightUnit;
let workout = WorkoutBuilder::new("w1")
.sport("strength")
.date("2026-03-29")
.root(session("Upper Hypertrophy", |s| {
s.block(ExecutionMode::Parallel, |b| {
b.name("Superset A")
.rest(90.0)
.exercise("DB Row", |e| {
e.set(vec![weight(30.0, WeightUnit::Kg), reps(10)], Some(rpe(8.0)));
})
.exercise("Incline Press", |e| {
e.set(vec![weight(25.0, WeightUnit::Kg), reps(12)], None);
});
});
}))
.build();
let json = serde_json::to_string_pretty(&workout).unwrap();
```
**Node.js** (parse + validate)
```js
import { readFileSync } from 'fs';
import { parseWorkout, validateWorkout, countSets } from '@syntax-workout/sdk';
const json = readFileSync('examples/strength/supersets.json', 'utf-8');
// Parse and round-trip through Rust types
const workout = JSON.parse(parseWorkout(json));
console.log(workout.sport); // "strength"
// Validate only (returns error strings, empty = valid)
const errors = validateWorkout(json);
// Count total sets
const sets = countSets(json);
```
**Python** (parse + validate)
```python
import syntax_workout_sdk
json_str = open("examples/strength/supersets.json").read()
# Parse into Python dict
workout = syntax_workout_sdk.parse_workout(json_str)
print(workout["sport"]) # "strength"
# Validate only
errors = syntax_workout_sdk.validate_workout(json_str)
# Count sets
count = syntax_workout_sdk.count_sets(json_str)
```
## ExecutionMode
Controls how children of a `Block` node are performed.
| `Sequential` | Finish all sets of exercise A, then B | Straight sets |
| `Parallel` | Alternate sets across exercises | Supersets, giant sets |
| `Circuit { rounds: N }` | Cycle through all exercises N times | Circuit training |
| `Custom(String)` | User-defined | AMRAP, EMOM |
## Measure variants
| `Reps` | `u32` | `Reps(10)` |
| `Weight` | `value: f64, unit: Kg\|Lbs` | `Weight { 80.0, Kg }` |
| `Distance` | `value: f64, unit: Meters\|Kilometers\|Miles` | `Distance { 5.0, Km }` |
| `Duration` | `seconds: f64` | `Duration { 180.0 }` |
| `Pace` | `per: DistanceUnit, seconds: f64` | `Pace { Km, 330.0 }` |
| `HeartRate` | `bpm: u32` | `HeartRate { 145 }` |
| `Calories` | `u32` | `Calories(350)` |
| `Custom` | `name: String, value: Value` | `Custom { "elevation", 150 }` |
## Intensity variants
| `PercentOfMax` | `f64` (0.0-1.0) | `PercentOfMax(0.80)` |
| `RPE` | `f64` (1-10) | `RPE(8.5)` |
| `RIR` | `u32` | `RIR(2)` |
| `Bodyweight` | -- | `Bodyweight` |
| `Custom` | `name: String, value: Value` | `Custom { "zone", 3 }` |
## Examples
See [`examples/`](examples/) for JSON workout files covering strength, endurance, and mixed modalities.
## License
MIT OR Apache-2.0