syntax-workout-core 0.2.0

Workout tree algebra — represent any physical workout as a recursive tree
Documentation
# syntax-workout-sdk

Workout tree algebra -- represent any physical workout as a recursive tree.

[![crates.io](https://img.shields.io/crates/v/syntax-workout-core.svg)](https://crates.io/crates/syntax-workout-core)
[![npm](https://img.shields.io/npm/v/@syntax-workout/sdk.svg)](https://www.npmjs.com/package/@syntax-workout/sdk)
[![PyPI](https://img.shields.io/pypi/v/syntax-workout-sdk.svg)](https://pypi.org/project/syntax-workout-sdk/)
[![CI](https://github.com/nicetomeetyu/syntax-workout-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/nicetomeetyu/syntax-workout-sdk/actions/workflows/ci.yml)
[![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](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.

| Mode | Meaning | Example |
|------|---------|---------|
| `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

| Variant | Fields | Example |
|---------|--------|---------|
| `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

| Variant | Fields | Example |
|---------|--------|---------|
| `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