[OCaml] High Intensity Training Online
1
(** Serialization of import batches and promoted records for durable storage.
2
3
The import use case is provisional: a batch holds raw third-party source,
4
per-workout mappings and acknowledgements, and any promotions made so far. A
5
repository must store and restore that state without the third-party parser,
6
so this module encodes {!Hito_app.Workout_import.snapshot} — the parser-free
7
view — and {!Hito_app.Repository.imported_record}, the canonical promoted
8
evidence.
9
10
The wire format is a length-prefixed field framing: every field is written
11
as its byte length, a colon, then its raw bytes. The writer counts the
12
length in bytes and copies the bytes verbatim. A field may therefore hold
13
arbitrary UTF-8, newlines, commas, or colons. The format preserves the
14
export source, an acknowledgement, a title, or an unsupported-row reason
15
exactly. No {!Marshal}, no {!Obj}: the format is textual and
16
self-describing.
17
18
Timestamps reuse {!Timestamp}. Exercise ids resolve through
19
{!Hito_core.Exercise.find_id}. A decode failure is a module-specific
20
{!error}. A repository turns it into a fault, never a silent loss. *)
21
22
open Hito_app
23
24
type error =
25
| Malformed of string (** The stored text does not parse. *)
26
| Unknown_exercise of string
27
(** A stored exercise id is not in the catalog. *)
28
| Invalid_time (** A stored [ended_at] precedes its [started_at]. *)
29
30
val pp_error : Format.formatter -> error -> unit
31
32
val encode_snapshot : Workout_import.snapshot -> string
33
(** A stable, single-string encoding of an import batch snapshot. *)
34
35
val decode_snapshot :
36
find_exercise:(Exercise.id -> Exercise.t option) ->
37
string ->
38
(Workout_import.batch, error) result
39
(** Decode a batch snapshot and restore the opaque batch, resolving each
40
exercise id through [find_exercise]. *)
41
42
val encode_record : Repository.imported_record -> string
43
(** A stable, single-string encoding of a promoted record. *)
44
45
val decode_record : string -> (Repository.imported_record, error) result
46
(** Decode a promoted record, resolving each observation's exercise id through
47
{!Hito_core.Exercise.find_id}. *)
48