Skip to content

Commit 34af0e2

Browse files
committed
Stabilize UnitMath v1.0.0
1 parent 2618c08 commit 34af0e2

9 files changed

Lines changed: 363 additions & 82 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
[package]
22
name = "unitmath"
3-
version = "0.8.0"
3+
version = "1.0.0"
44
edition = "2021"
5+
description = "A small Rust library and CLI for unit conversions, parsing, and package math."
6+
license = "MIT"
7+
repository = "https://github.com/LJrobinson/UnitMath"
8+
keywords = ["units", "conversion", "measurement", "parsing", "cli"]
9+
categories = ["command-line-utilities", "parser-implementations", "science", "mathematics"]
510

611
[dependencies]

README.md

Lines changed: 110 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,163 @@
11
# UnitMath
22

3-
UnitMath is a small Rust library for pure unit conversion utilities.
3+
UnitMath is a small Rust library and dependency-free CLI for unit conversions, parsing, and package math.
44

5-
Version 0.8.0 supports weight, US liquid volume, potency conversions, package math helpers, basic string parsing, and parsed conversion helpers.
5+
Version 1.0.0 is a stable MVP focused on:
6+
7+
- Weight conversions
8+
- US liquid volume conversions
9+
- Potency conversions
10+
- Simple package math helpers
11+
- Basic string parsing for weight, volume, and potency quantities
12+
- Parsed conversion helpers
13+
- A minimal CLI
14+
15+
UnitMath intentionally does not include universal conversion, package parsing, JSON output, or trait-based quantity abstractions yet.
616

717
## Supported Units
818

919
### Weight
1020

11-
- Milligram
12-
- Gram
13-
- Kilogram
14-
- Ounce
15-
- Pound
21+
- Milligram: `mg`
22+
- Gram: `g`, `gram`, `grams`
23+
- Kilogram: `kg`
24+
- Ounce: `oz`, `ounce`, `ounces`
25+
- Pound: `lb`, `lbs`, `pound`, `pounds`
1626

1727
Weight conversions use grams as the canonical base unit internally.
1828

1929
### Volume
2030

21-
- Milliliter
22-
- Liter
23-
- FluidOunce
24-
- Cup
25-
- Pint
26-
- Quart
27-
- Gallon
31+
- Milliliter: `ml`, `milliliter`, `milliliters`
32+
- Liter: `l`, `liter`, `liters`
33+
- US fluid ounce: `fl oz`, `floz`, `fluid ounce`, `fluid ounces`
34+
- US cup: `cup`, `cups`
35+
- US pint: `pint`, `pints`
36+
- US quart: `quart`, `quarts`
37+
- US gallon: `gallon`, `gallons`
2838

2939
Volume conversions use milliliters as the canonical base unit internally.
3040

3141
### Potency
3242

33-
- Percent
34-
- MilligramsPerGram
43+
- Percent: `%`, `percent`, `percentage`
44+
- Milligrams per gram: `mg/g`, `mgg`, `mg per g`, `mg/g dry weight`, `milligrams per gram`
3545

3646
Potency conversions use milligrams per gram as the canonical base unit internally.
3747

38-
### Package Math
39-
40-
- `calculate_total_units(container_count, units_per_container)`
41-
- `calculate_total_quantity(unit_count, quantity_per_unit)`
48+
## Library Usage
4249

43-
Package math helpers multiply counts by per-unit quantities without applying unit conversion.
50+
```rust
51+
use unitmath::{
52+
calculate_total_quantity, calculate_total_units, convert_parsed_potency,
53+
convert_parsed_volume, convert_parsed_weight, convert_potency, convert_volume,
54+
convert_weight, parse_potency, parse_volume, parse_weight, PotencyUnit, VolumeUnit,
55+
WeightUnit,
56+
};
4457

45-
### Weight Parsing
58+
let grams = convert_weight(1000.0, WeightUnit::Milligram, WeightUnit::Gram);
59+
assert_eq!(grams, 1.0);
4660

47-
- `parse_weight("1000 mg")`
48-
- `parse_weight("1000mg")`
49-
- `parse_weight("3.5 g")`
50-
- `parse_weight("2 pounds")`
61+
let liters = convert_volume(1000.0, VolumeUnit::Milliliter, VolumeUnit::Liter);
62+
assert_eq!(liters, 1.0);
5163

52-
Weight parsing is case-insensitive and returns a `ParsedQuantity<WeightUnit>`.
64+
let milligrams_per_gram =
65+
convert_potency(22.4, PotencyUnit::Percent, PotencyUnit::MilligramsPerGram);
66+
assert_eq!(milligrams_per_gram, 224.0);
5367

54-
### Volume Parsing
68+
let units = calculate_total_units(2.0, 12.0);
69+
assert_eq!(units, 24.0);
5570

56-
- `parse_volume("1000 ml")`
57-
- `parse_volume("1000ml")`
58-
- `parse_volume("8 fl oz")`
59-
- `parse_volume("2 gallons")`
71+
let total_grams = calculate_total_quantity(10.0, 3.5);
72+
assert_eq!(total_grams, 35.0);
6073

61-
Volume parsing is case-insensitive and returns a `ParsedQuantity<VolumeUnit>`.
74+
let parsed_weight = parse_weight(" 3.5G ")?;
75+
assert_eq!(parsed_weight.value, 3.5);
76+
assert_eq!(parsed_weight.unit, WeightUnit::Gram);
6277

63-
### Potency Parsing
78+
let parsed_volume = parse_volume("8 fl oz")?;
79+
assert_eq!(parsed_volume.unit, VolumeUnit::FluidOunce);
6480

65-
- `parse_potency("22.4%")`
66-
- `parse_potency("22.4 percent")`
67-
- `parse_potency("224 mg/g")`
68-
- `parse_potency("224 milligrams per gram")`
81+
let parsed_potency = parse_potency("22.4%")?;
82+
assert_eq!(parsed_potency.unit, PotencyUnit::Percent);
6983

70-
Potency parsing is case-insensitive and returns a `ParsedQuantity<PotencyUnit>`.
84+
let grams = convert_parsed_weight("1000mg", WeightUnit::Gram)?;
85+
assert_eq!(grams, 1.0);
7186

72-
### Parsed Conversion Helpers
87+
let cups = convert_parsed_volume("8 fl oz", VolumeUnit::Cup)?;
88+
assert_eq!(cups, 1.0);
7389

74-
- `convert_parsed_weight("1000mg", WeightUnit::Gram)`
75-
- `convert_parsed_volume("8 fl oz", VolumeUnit::Cup)`
76-
- `convert_parsed_potency("22.4%", PotencyUnit::MilligramsPerGram)`
90+
let milligrams_per_gram =
91+
convert_parsed_potency("22.4%", PotencyUnit::MilligramsPerGram)?;
92+
assert_eq!(milligrams_per_gram, 224.0);
7793

78-
Parsed conversion helpers parse a quantity string and convert it to a target unit in one step.
94+
# Ok::<(), unitmath::UnitMathError>(())
95+
```
7996

80-
## Usage
97+
## Parsing Examples
8198

8299
```rust
83-
use unitmath::{
84-
calculate_total_quantity, calculate_total_units, convert_potency, convert_volume,
85-
convert_parsed_potency, convert_parsed_volume, convert_parsed_weight, convert_weight,
86-
parse_potency, parse_volume, parse_weight, PotencyUnit, VolumeUnit, WeightUnit,
87-
};
100+
use unitmath::{parse_potency, parse_volume, parse_weight, PotencyUnit, VolumeUnit, WeightUnit};
88101

89-
let grams = convert_weight(1000.0, WeightUnit::Milligram, WeightUnit::Gram);
90-
assert_eq!(grams, 1.0);
102+
assert_eq!(parse_weight("1000 mg")?.unit, WeightUnit::Milligram);
103+
assert_eq!(parse_weight("1000mg")?.unit, WeightUnit::Milligram);
104+
assert_eq!(parse_weight(".5 g")?.value, 0.5);
91105

92-
let pounds = convert_weight(16.0, WeightUnit::Ounce, WeightUnit::Pound);
93-
assert!((pounds - 1.0).abs() < 1e-12);
106+
assert_eq!(parse_volume("1000 ml")?.unit, VolumeUnit::Milliliter);
107+
assert_eq!(parse_volume("8floz")?.unit, VolumeUnit::FluidOunce);
108+
assert_eq!(parse_volume("2 gallons")?.unit, VolumeUnit::Gallon);
94109

95-
let liters = convert_volume(1000.0, VolumeUnit::Milliliter, VolumeUnit::Liter);
96-
assert!((liters - 1.0).abs() < 1e-12);
110+
assert_eq!(parse_potency("22.4 %")?.unit, PotencyUnit::Percent);
111+
assert_eq!(parse_potency("224mg/g")?.unit, PotencyUnit::MilligramsPerGram);
112+
assert_eq!(
113+
parse_potency("224 milligrams per gram")?.unit,
114+
PotencyUnit::MilligramsPerGram
115+
);
97116

98-
let milligrams_per_gram = convert_potency(22.4, PotencyUnit::Percent, PotencyUnit::MilligramsPerGram);
99-
assert!((milligrams_per_gram - 224.0).abs() < 1e-12);
117+
# Ok::<(), unitmath::UnitMathError>(())
118+
```
100119

101-
let units = calculate_total_units(2.0, 12.0);
102-
assert_eq!(units, 24.0);
120+
Parsing trims whitespace, is case-insensitive, and returns `UnitMathError` for empty input, missing numbers, invalid numbers, missing units, and unknown units.
103121

104-
let grams = calculate_total_quantity(10.0, 3.5);
105-
assert_eq!(grams, 35.0);
122+
## Package Helpers
106123

107-
let parsed = parse_weight("3.5 g").unwrap();
108-
assert_eq!(parsed.value, 3.5);
109-
assert_eq!(parsed.unit, WeightUnit::Gram);
124+
```rust
125+
use unitmath::{calculate_total_quantity, calculate_total_units};
110126

111-
let parsed = parse_volume("8 fl oz").unwrap();
112-
assert_eq!(parsed.value, 8.0);
113-
assert_eq!(parsed.unit, VolumeUnit::FluidOunce);
127+
let units = calculate_total_units(5.0, 24.0);
128+
assert_eq!(units, 120.0);
114129

115-
let parsed = parse_potency("22.4%").unwrap();
116-
assert_eq!(parsed.value, 22.4);
117-
assert_eq!(parsed.unit, PotencyUnit::Percent);
130+
let total_milligrams = calculate_total_quantity(24.0, 100.0);
131+
assert_eq!(total_milligrams, 2400.0);
132+
```
118133

119-
let grams = convert_parsed_weight("1000mg", WeightUnit::Gram).unwrap();
120-
assert_eq!(grams, 1.0);
134+
Package helpers only multiply counts by per-unit quantities. They do not parse package strings or perform unit conversion.
121135

122-
let cups = convert_parsed_volume("8 fl oz", VolumeUnit::Cup).unwrap();
123-
assert!((cups - 1.0).abs() < 1e-12);
136+
## CLI Usage
124137

125-
let milligrams_per_gram =
126-
convert_parsed_potency("22.4%", PotencyUnit::MilligramsPerGram).unwrap();
127-
assert_eq!(milligrams_per_gram, 224.0);
138+
```sh
139+
unitmath weight "1000mg" g
140+
unitmath weight "1 lb" oz
141+
unitmath volume "8 fl oz" cup
142+
unitmath volume "1 gallon" ml
143+
unitmath potency "22.4%" mg/g
144+
unitmath potency "224mg/g" percent
145+
```
146+
147+
Each command prints only the numeric converted value on success. Errors are written to stderr with usage guidance.
148+
149+
## Examples
150+
151+
```sh
152+
cargo run --example basic_weight
153+
cargo run --example basic_volume
154+
cargo run --example basic_potency
128155
```
129156

130-
## Roadmap
157+
## Roadmap After v1.0.0
131158

132159
- Additional parsers
133-
- CLI
160+
- Package parsing
161+
- More unit families
162+
- Optional structured output modes
163+
- Broader CLI ergonomics

examples/basic_potency.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use unitmath::{convert_parsed_potency, convert_potency, parse_potency, PotencyUnit};
2+
3+
fn main() -> Result<(), unitmath::UnitMathError> {
4+
let milligrams_per_gram =
5+
convert_potency(22.4, PotencyUnit::Percent, PotencyUnit::MilligramsPerGram);
6+
println!("22.4% = {milligrams_per_gram} mg/g");
7+
8+
let parsed = parse_potency("224 mg/g")?;
9+
println!("parsed potency: {} {:?}", parsed.value, parsed.unit);
10+
11+
let percent = convert_parsed_potency("224mg/g", PotencyUnit::Percent)?;
12+
println!("224 mg/g = {percent}%");
13+
14+
Ok(())
15+
}

examples/basic_volume.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use unitmath::{convert_parsed_volume, convert_volume, parse_volume, VolumeUnit};
2+
3+
fn main() -> Result<(), unitmath::UnitMathError> {
4+
let liters = convert_volume(1000.0, VolumeUnit::Milliliter, VolumeUnit::Liter);
5+
println!("1000 ml = {liters} l");
6+
7+
let parsed = parse_volume("8 fl oz")?;
8+
println!("parsed volume: {} {:?}", parsed.value, parsed.unit);
9+
10+
let cups = convert_parsed_volume("8 fl oz", VolumeUnit::Cup)?;
11+
println!("8 fl oz = {cups} cup");
12+
13+
Ok(())
14+
}

examples/basic_weight.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use unitmath::{convert_parsed_weight, convert_weight, parse_weight, WeightUnit};
2+
3+
fn main() -> Result<(), unitmath::UnitMathError> {
4+
let grams = convert_weight(1000.0, WeightUnit::Milligram, WeightUnit::Gram);
5+
println!("1000 mg = {grams} g");
6+
7+
let parsed = parse_weight("3.5 g")?;
8+
println!("parsed weight: {} {:?}", parsed.value, parsed.unit);
9+
10+
let ounces = convert_parsed_weight("3.5g", WeightUnit::Ounce)?;
11+
println!("3.5 g = {ounces} oz");
12+
13+
Ok(())
14+
}

src/error.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::{error::Error, fmt};
2+
13
/// Errors returned by UnitMath parsing helpers.
24
#[derive(Debug, Clone, PartialEq, Eq)]
35
pub enum UnitMathError {
@@ -12,3 +14,19 @@ pub enum UnitMathError {
1214
/// The unit string is not supported.
1315
UnknownUnit,
1416
}
17+
18+
impl fmt::Display for UnitMathError {
19+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
20+
let message = match self {
21+
Self::EmptyInput => "input is empty",
22+
Self::MissingNumber => "missing numeric value",
23+
Self::InvalidNumber => "invalid numeric value",
24+
Self::MissingUnit => "missing unit",
25+
Self::UnknownUnit => "unknown unit",
26+
};
27+
28+
formatter.write_str(message)
29+
}
30+
}
31+
32+
impl Error for UnitMathError {}

0 commit comments

Comments
 (0)