(* The shell exit applicative? This is very like the result monad, except modelled around a shell's exit status codes *) type should_exit = { interactive : [ `Yes | `No ]; non_interactive : [ `Yes | `No ]; } let default_should_exit = { interactive = `No; non_interactive = `Yes } type 'a t = | Zero of 'a | Nonzero of { value : 'a; exit_code : int; message : string option; should_exit : should_exit; } let ignore = function | Zero _ -> Zero () | Nonzero v -> Nonzero { v with value = () } let value = function Zero v -> v | Nonzero { value; _ } -> value let not = function | Zero value -> Nonzero { value; exit_code = 1; message = None; should_exit = default_should_exit; } | Nonzero { value; _ } -> Zero value let zero v = Zero v let nonzero ?message ?(should_exit = default_should_exit) value exit_code = Nonzero { value; exit_code; message; should_exit } let code = function Zero _ -> 0 | Nonzero { exit_code; _ } -> exit_code let nonzero_msg ?(exit_code = 1) ?(should_exit = default_should_exit) value fmt = Fmt.kstr (fun message -> Nonzero { value; exit_code; message = Some message; should_exit }) fmt let map ~f = function | Zero v -> Zero (f v) | Nonzero ({ value; _ } as v) -> Nonzero { v with value = f value } let map_zero ~f = function Zero v -> f v | Nonzero x -> Nonzero x let map' ~zero ~nonzero = function | Zero v -> Zero (zero v) | Nonzero v -> Nonzero { v with value = nonzero v.value } let is_nonzero = function Zero _ -> false | Nonzero _ -> true let is_zero = function Zero _ -> true | Nonzero _ -> false let pp ppf = function | Zero _ -> Fmt.string ppf "zero" | Nonzero _ -> Fmt.string ppf "nonzero" module Syntax = struct let ( >|= ) x f = map ~f x let ( >>= ) x f = map_zero ~f x let ( let+ ) = ( >|= ) end