qtty-ffi 0.2.2

py & C-compatible FFI bindings for qtty physical quantities and unit conversions
Documentation
# FFI Unit Definitions

This file defines all units exposed through the qtty-ffi C API.

## Format

```
discriminant,dimension,name,symbol,ratio
```

### Discriminant Encoding

Discriminants use a **DSSCC** format:
- **D** (1 digit): Dimension (1=Length, 2=Time, 3=Angle, 4=Mass, 5=Power)
- **SS** (2 digits): System/Category code (00, 10, 20, 30, etc.)
- **CC** (2 digits): Counter within that system (00-99)

**Example:** `21000` = Minute (Dimension=2 [Time], System=10 [Common], Counter=00)

### Fields

- **discriminant**: Unique u32 ID (ABI-stable, NEVER change once assigned) - sorted for easy lookup
- **dimension**: One of: `Length`, `Time`, `Angle`, `Mass`, `Power` (groups units by type)
- **name**: Rust identifier (PascalCase, no spaces)
- **symbol**: Display symbol (can include Unicode)
- **ratio**: Conversion factor to canonical unit (exact or best-available)

### Canonical Units

Each dimension has a canonical unit (ratio = 1.0):

- **Length**: Meter (discriminant 10011)
- **Time**: Second (discriminant 20008)  
- **Angle**: Radian (discriminant 30001)
- **Mass**: Gram (discriminant 40010)
- **Power**: Watt (discriminant 50009)

### Discriminant Ranges

Units are grouped by dimension and system:

**Length (1xxxx):**
- 100xx: SI metric (Meter, Kilometer, etc.)
- 110xx: Astronomical (AU, LightYear, Parsec, etc.)
- 120xx: Imperial (Inch, Foot, Mile, etc.)
- 130xx: Nautical (NauticalMile, Fathom, etc.)
- 150xx: Nominal (SolarRadius, EarthRadius, etc.)

**Time (2xxxx):**
- 200xx: SI metric (Second, Millisecond, etc.)
- 210xx: Common (Minute, Hour, Day, Week, etc.)
- 220xx: Calendar (Year, Decade, Century, etc.)
- 230xx: Astronomical (SiderealDay, SiderealYear, etc.)

**Angle (3xxxx):**
- 300xx: Radian-based (Radian, Milliradian)
- 310xx: Degree-based (Degree, Arcminute, Arcsecond, etc.)
- 320xx: Other (Gradian, Turn, HourAngle)

**Mass (4xxxx):**
- 400xx: SI metric (Gram, Kilogram, etc.)
- 410xx: Imperial (Pound, Ounce, Stone, Ton, etc.)
- 420xx: Special (Carat, AtomicMassUnit, SolarMass, etc.)

**Power (5xxxx):**
- 500xx: SI metric (Watt, Kilowatt, etc.)
- 510xx: Other (Horsepower, SolarLuminosity, etc.)

### ABI Stability

**CRITICAL**: Once a discriminant is assigned and released, it MUST NEVER CHANGE.
This is a C ABI contract. Changing discriminants breaks binary compatibility.

When adding new units:
1. Choose an unused discriminant in the appropriate range
2. Add a new line to this file
3. Commit and release

## Adding a New Unit

Example: Adding Angstrom (10^-10 meters) to SI Length units

```csv
10022,Length,Angstrom,Å,1e-10
```

The discriminant breaks down as:
- `1` = Length dimension
- `00` = SI system  
- `22` = Counter (next available after Yottameter at 21)

Then rebuild: `cargo build`

The build script will automatically generate all necessary Rust code.

## Benefits of CSV Approach

✅ **Simple**: No AST parsing, just read CSV  
✅ **Explicit**: All units visible in one file  
✅ **Reviewable**: Git diffs show exactly what changed  
✅ **Maintainable**: Easy to add/modify units  
✅ **Debuggable**: Parse errors point to specific lines  
✅ **Independent**: FFI doesn't depend on qtty-core internals  

## Validation

The build script validates:
- Each line has exactly 5 fields
- Discriminants are valid u32 integers
- Dimension names match known dimensions

Invalid lines are skipped with a warning during build.