type t = { name : Branch.t; submission : Submission.t; tip : Commit.t; target : Branch.t; since : Commit.t; onto : Branch.t option; keep_branch : bool; retry : bool; dry_run : bool; for_ : Feature_owner.t option; resolve : bool option; expedite : string option; repaired : Check.name list; model_repaired : bool; } let v ~submission ~name ~tip ~target ~since ?onto ?for_ ?resolve ?expedite ?(repaired = []) ?(model_repaired = false) ?(dry_run = false) ~keep_branch ~retry () = (match expedite with | Some r when String.contains r '\n' -> invalid_arg "Entry.v: an expedite reason holds a newline" | Some _ | None -> ()); { submission; name; tip; target; since; onto; keep_branch; retry; dry_run; for_; resolve; expedite; repaired; model_repaired; } let upstream e = match e.onto with None -> Some e.target | Some _ -> None let description e = match Description.to_string e.submission.description with | "" -> None | d -> Some d let message e = let open Record in empty |> add "target" (Branch.to_string e.target) |> add "since" (Commit.to_hex e.since) |> (match e.onto with | None -> Fun.id | Some b -> add "onto" (Branch.to_string b)) |> flag "keep-branch" e.keep_branch |> flag "retry" e.retry |> flag "dry-run" e.dry_run |> flag "resolve" (e.resolve = Some true) |> flag "no-resolve" (e.resolve = Some false) |> (match e.for_ with | None -> Fun.id | Some owner -> add "for" (Feature_owner.field owner)) |> (match e.expedite with None -> Fun.id | Some r -> add "expedite" r) |> (fun fs -> List.fold_left (fun fs c -> add "repaired" (Check.string_of_name c) fs) fs e.repaired) |> flag "model-repaired" e.model_repaired |> add "submission" (Submission.to_string e.submission) |> Record.message Entry let ( let* ) = Result.bind let resolve_choice fs = let* resolve = Record.has "resolve" fs in let* no_resolve = Record.has "no-resolve" fs in match (resolve, no_resolve) with | true, true -> Error (Record.Malformed "both resolve and no-resolve") | true, false -> Ok (Some true) | false, true -> Ok (Some false) | false, false -> Ok None let options fs = let* onto = Record.optional "onto" Record.branch fs in let* keep_branch = Record.has "keep-branch" fs in let* retry = Record.has "retry" fs in let* dry_run = Record.has "dry-run" fs in let* for_ = Record.optional "for" Feature_owner.of_canonical_field fs in let* resolve = resolve_choice fs in let* expedite = Record.optional "expedite" Result.ok fs in Ok (onto, keep_branch, retry, dry_run, for_, resolve, expedite) let of_tag (tag : Record.tag) = let* fs = Record.fields Entry tag.message in let* () = Record.only [ "submission"; "target"; "since"; "onto"; "keep-branch"; "retry"; "dry-run"; "for"; "resolve"; "no-resolve"; "expedite"; "repaired"; "model-repaired"; ] fs in let* name = Result.map_error (fun m -> Record.Malformed m) (Record.branch tag.name) in let* target = Record.required "target" Record.branch fs in let* since = Record.required "since" Record.commit fs in let* onto, keep_branch, retry, dry_run, for_, resolve, expedite = options fs in let* submission = Record.required "submission" Submission.of_string fs in let* repaired = Record.all "repaired" (fun s -> Result.map_error (fun (`Msg m) -> m) (Check.name_of_string s)) fs in let* model_repaired = Record.has "model-repaired" fs in Ok { submission; name; tip = Commit.of_hash tag.target; target; since; onto; keep_branch; retry; dry_run; for_; resolve; expedite; repaired; model_repaired; } let same_land a b = Branch.equal a.name b.name && Commit.equal a.tip b.tip && Branch.equal a.target b.target && Option.equal Branch.equal a.onto b.onto && Bool.equal a.keep_branch b.keep_branch && Bool.equal a.retry b.retry && Bool.equal a.dry_run b.dry_run && Option.equal Feature_owner.equal a.for_ b.for_ let equal a b = same_land a b && Submission.equal a.submission b.submission && Commit.equal a.since b.since && Option.equal Bool.equal a.resolve b.resolve && Option.equal String.equal a.expedite b.expedite && List.equal Check.equal_name a.repaired b.repaired && Bool.equal a.model_repaired b.model_repaired let pp ppf e = Fmt.pf ppf "%a at %s for %a" Branch.pp e.name (Commit.short e.tip) Branch.pp e.target