View raw

1 module Form = Dream_html.Form 2 3 type fields = 4 | Single of { 5 load : float; 6 reps : int; 7 extension : Evidence.Stimulus.extension option; 8 } 9 | Pair of { 10 iso_load : float; 11 iso_reps : int; 12 comp_load : float; 13 comp_reps : int; 14 extension : Evidence.Stimulus.extension option; 15 } 16 17 let extension = function 18 | "" -> Ok None 19 | "forced" -> Ok (Some Evidence.Stimulus.Forced_reps) 20 | "negatives" -> Ok (Some Evidence.Stimulus.Negatives) 21 | "rest-pause" -> Ok (Some Evidence.Stimulus.Rest_pause) 22 | "static" -> Ok (Some Evidence.Stimulus.Static_hold) 23 | _ -> Error "error.extension" 24 25 let load value = 26 match Form.float ~min:0. value with 27 | Ok value -> Ok value 28 | Error "error.expected.int" -> Error "error.expected.number" 29 | Error _ -> Error "error.load" 30 31 let reps value = 32 match Form.int value with 33 | Error error -> Error error 34 | Ok value when value > 0 -> Ok value 35 | Ok _ -> Error "error.reps" 36 37 let fields prescription = 38 let open Form in 39 match Prescription.Stimulus.delivery prescription with 40 | Prescription.Stimulus.Single _ -> 41 let+ load = required load "load" 42 and+ reps = required reps "reps" 43 and+ extension = required extension "extension" in 44 Single { load; reps; extension } 45 | Prescription.Stimulus.Pre_exhaust _ -> 46 let+ iso_load = required load "iso_load" 47 and+ iso_reps = required reps "iso_reps" 48 and+ comp_load = required load "comp_load" 49 and+ comp_reps = required reps "comp_reps" 50 and+ extension = required extension "extension" in 51 Pair { iso_load; iso_reps; comp_load; comp_reps; extension } 52 53 let stimulus prescription = 54 let open Form in 55 let* values = fields prescription in 56 match (Prescription.Stimulus.delivery prescription, values) with 57 | Prescription.Stimulus.Single exercise, Single { load; reps; extension } -> 58 let outcome = 59 match extension with 60 | None -> Evidence.Stimulus.Positive_failure 61 | Some extension -> Evidence.Stimulus.Beyond_failure (extension, []) 62 in 63 ok 64 (Evidence.Stimulus.make 65 (Evidence.Stimulus.Single 66 (Evidence.Stimulus.Effort.make ~exercise ~load ~reps ~outcome))) 67 | ( Prescription.Stimulus.Pre_exhaust { isolation; compound }, 68 Pair { iso_load; iso_reps; comp_load; comp_reps; extension } ) -> 69 let second_outcome = 70 match extension with 71 | None -> Evidence.Stimulus.Positive_failure 72 | Some extension -> Evidence.Stimulus.Beyond_failure (extension, []) 73 in 74 let first = 75 Evidence.Stimulus.Effort.make ~exercise:isolation ~load:iso_load 76 ~reps:iso_reps ~outcome:Evidence.Stimulus.Positive_failure 77 in 78 let second = 79 Evidence.Stimulus.Effort.make ~exercise:compound ~load:comp_load 80 ~reps:comp_reps ~outcome:second_outcome 81 in 82 ok (Evidence.Stimulus.make (Evidence.Stimulus.Pair { first; second })) 83 | _ -> assert false 84 85 let override = Form.required Form.bool "override" 86 87 (* --- profile --- *) 88 89 (* The raw username string; the domain normalizes and validates it downstream. *) 90 let profile_username = Form.required Form.string "username" 91 92 (* Current and new password, both required. Passwords carry no policy here; the 93 service verifies the current one before storing the new one. *) 94 let profile_password = 95 let open Form in 96 let+ current = required string "current" and+ next = required string "next" in 97 (current, next) 98 99 (* --- application feedback --- *) 100 101 let app_feedback = Form.required Form.string "message" 102 103 (* --- error presentation --- *) 104 105 let message = function 106 | "error.required" -> "Enter a value." 107 | "error.expected.number" -> "Enter a valid number." 108 | "error.expected.int" -> "Enter a valid whole number." 109 | "error.reps" -> "Enter at least one repetition." 110 | "error.load" -> "Enter a finite, nonnegative load." 111 | "error.extension" -> "Select one of the offered endings." 112 | key -> key 113 114 let errors_to_text errors = 115 errors |> List.map (fun (_, key) -> message key) |> String.concat " " 116