View raw

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