aboutsummaryrefslogtreecommitdiffstats
path: root/lib/concepts/BranchState.ml
blob: e8c66369a3736dc1228cfa33fea9608070f41602 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
type address = Hash.hash

(* A branch state records durable branch data only. The runtime registry paths
   mounted around it, such as /system/branches/master, are derived at
   initialization and should not be persisted as part of [current]. *)
type t = {current: address; history: address; tip: address option}

module Field = struct
  let current = "current"
  let history = "history"
  let tip = "tip"
end

module Error = struct
  open Condition

  let malformed_branch_state () =
    condition "branch-state-malformed"
      "A branch state representation did not conform to what was expected" empty

  let malformed_hash length =
    condition "branch-state-malformed-hash" "A branch state address was not a valid hash"
      ("length" |=| Value.Integer length)
end

let make ~current ~history ~tip = {current; history; tip}
let current branch_state = branch_state.current
let history branch_state = branch_state.history
let tip branch_state = branch_state.tip
let address_bencode address = Object.Field.address address

let address_option_bencode = function
  | None -> Codec.Bencode.List []
  | Some address -> Codec.Bencode.List [address_bencode address]

let require_address_bencode = function
  | Codec.Bencode.String raw ->
      if String.length raw = Hash.size then Ok (Hash.of_raw_string raw)
      else Error (Error.malformed_hash (String.length raw))
  | _ -> Error (Error.malformed_branch_state ())

let require_address_option key data =
  let open Utilities.Result in
  let* value = Codec.Bencode.field key data in
  let* entries = Codec.Bencode.as_list value in
  match entries with
  | [] -> Ok None
  | [value] ->
      let* address = require_address_bencode value in
      Ok (Some address)
  | _ -> Error (Error.malformed_branch_state ())

let equal_address_option left right =
  match left, right with
  | None, None -> true
  | Some left, Some right -> Hash.hash_equals left right
  | _ -> false

module Body = struct
  type nonrec t = t

  let tag = 's'
  let malformed = Error.malformed_branch_state

  let equal left right =
    Hash.hash_equals left.current right.current
    && Hash.hash_equals left.history right.history
    && equal_address_option left.tip right.tip

  let fields branch_state =
    [ Field.current, address_bencode branch_state.current;
      Field.history, address_bencode branch_state.history;
      Field.tip, address_option_bencode branch_state.tip ]

  let of_fields data =
    let open Utilities.Result in
    let* current =
      Object.Field.require_address ~malformed:Error.malformed_hash Field.current data
    in
    let* history =
      Object.Field.require_address ~malformed:Error.malformed_hash Field.history data
    in
    let* tip = require_address_option Field.tip data in
    Ok {current; history; tip}
end

include (Object.Codec.Make (Body) : Object.S with type t := t)