feat implement Weight, Reps, Rep_range

Concrete representations: Weight.t and Rep_range.t's bounds are floats and ints under the hood, made safe only through the smart constructors the .mli exposes. Rep_range.t is a plain pair, since its invariant (min <= max) is checked once at construction and never needs re-establishing. Wires test_units into the runner, retiring the placeholder smoke test.

Commit
0a1ce528e529a0f744b7c2b766c25b4d04d75d50
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
lib/core/units.ml
index 6e43a753..5950a18c 100644..100644
@@ -1,37 +1,49 @@
1 Removed: (* Minimal stubs only; implementation deferred until after the review gate. *)
2 Removed:
3 1 type error = Negative | Not_positive | Inverted_range
4 2
5 Removed: let pp_error _ _ = failwith "TODO"
3 Added: let pp_error fmt = function
4 Added: | Negative -> Format.pp_print_string fmt "must be non-negative and finite"
5 Added: | Not_positive -> Format.pp_print_string fmt "must be strictly positive"
6 Added: | Inverted_range ->
7 Added: Format.pp_print_string fmt "range's minimum exceeds its maximum"
6 8
7 9 module Weight = struct
8 10 type t = float
9 11
10 Removed: let of_kg _ = failwith "TODO"
11 Removed: let to_kg _ = failwith "TODO"
12 Added: let of_kg kg =
13 Added: if Float.is_finite kg && kg >= 0.0 then Ok kg else Error Negative
14 Added:
15 Added: let to_kg t = t
12 16 let zero = 0.0
13 Removed: let compare _ _ = failwith "TODO"
14 Removed: let equal _ _ = failwith "TODO"
15 Removed: let pp _ _ = failwith "TODO"
17 Added: let compare = Float.compare
18 Added: let equal = Float.equal
19 Added: let pp fmt t = Format.fprintf fmt "%g kg" t
16 20 end
17 21
18 22 module Reps = struct
19 23 type t = int
20 24
21 Removed: let of_int _ = failwith "TODO"
22 Removed: let to_int _ = failwith "TODO"
23 Removed: let compare _ _ = failwith "TODO"
24 Removed: let equal _ _ = failwith "TODO"
25 Removed: let pp _ _ = failwith "TODO"
25 Added: let of_int n = if n > 0 then Ok n else Error Not_positive
26 Added: let to_int t = t
27 Added: let compare = Int.compare
28 Added: let equal = Int.equal
29 Added: let pp fmt t = Format.fprintf fmt "%d reps" t
26 30 end
27 31
28 32 module Rep_range = struct
29 33 type t = Reps.t * Reps.t
30 34
31 Removed: let make ~min:_ ~max:_ = failwith "TODO"
32 Removed: let min _ = failwith "TODO"
33 Removed: let max _ = failwith "TODO"
34 Removed: let contains _ _ = failwith "TODO"
35 Removed: let equal _ _ = failwith "TODO"
36 Removed: let pp _ _ = failwith "TODO"
35 Added: let make ~min ~max =
36 Added: if Reps.to_int min <= Reps.to_int max then Ok (min, max)
37 Added: else Error Inverted_range
38 Added:
39 Added: let min (lo, _) = lo
40 Added: let max (_, hi) = hi
41 Added:
42 Added: let contains (lo, hi) r =
43 Added: Reps.to_int lo <= Reps.to_int r && Reps.to_int r <= Reps.to_int hi
44 Added:
45 Added: let equal (lo1, hi1) (lo2, hi2) = Reps.equal lo1 lo2 && Reps.equal hi1 hi2
46 Added:
47 Added: let pp fmt (lo, hi) =
48 Added: Format.fprintf fmt "%d-%d reps" (Reps.to_int lo) (Reps.to_int hi)
37 49 end
test/test_hito.ml
index a076cdba..05e5b5f6 100644..100644
@@ -1,8 +1,4 @@
1 Removed: (** Test harness entry point.
1 Added: (** Test harness entry point. Per-module suites are registered here as their
2 Added: implementations land. *)
2 3
3 Removed: Per-module suites are registered here as their implementations land
4 Removed: ([Test_units.suite], [Test_exercise.suite], and so on). Until then this
5 Removed: smoke test confirms the harness links against the library. *)
6 Removed:
7 Removed: let smoke = [ ("links", `Quick, fun () -> ()) ]
8 Removed: let () = Alcotest.run "hito" [ ("harness", smoke) ]
4 Added: let () = Alcotest.run "hito" Test_units.suite