aboutsummaryrefslogblamecommitdiffstats
path: root/lib/concepts/Tuple.ml
blob: dc6eb5f410fd4f5d50b499804f488d1f87092d58 (plain) (tree)
























































































                                                                                              
type t = (string, Value.value) BatMap.t

let empty = BatMap.empty
let is_empty = BatMap.is_empty
let set = BatMap.add
let find_opt = BatMap.find_opt

module Error = struct
  open Condition

  let unknown_attribute name =
    condition "unknown-attribute" "No such attribute on this tuple"
      ("attribute" |=| Value.String name)
end

let get name tuple =
  match BatMap.find_opt name tuple with
  | Some v -> Ok v
  | None -> Error (Error.unknown_attribute name)

let attributes tuple = BatMap.foldi (fun name _ acc -> BatSet.add name acc) tuple BatSet.empty

let project names tuple =
  let open Utilities.Result in
  BatFingerTree.fold_left
    (fun acc name ->
      let* acc = acc in
      let* value = get name tuple in
      Ok (set name value acc))
    (Ok empty) names

let rename pairs tuple =
  let open Utilities.Result in
  BatFingerTree.fold_left
    (fun acc (old_name, new_name) ->
      let* acc = acc in
      let* value = get old_name tuple in
      Ok (set new_name value acc))
    (Ok empty) pairs

let merge left right =
  BatMap.foldi
    (fun name value acc ->
      match acc with
      | None -> None
      | Some acc -> (
         match BatMap.find_opt name acc with
         | None -> Some (BatMap.add name value acc)
         | Some value' -> if Value.equal value value' then Some acc else None))
    right (Some left)

(* Tuple's own wire format for a single value: kept local rather than
   added to Concepts.Encoding.Value (which only ever encodes hashes and
   options of them today) so this module owns its representation
   end to end. *)
let bencode_of_value = function
  | Value.String s -> Encoding.Bencode.Tagged ('s', Encoding.Bencode.String s)
  | Value.Integer n -> Encoding.Bencode.Tagged ('i', Encoding.Bencode.Int n)

let malformed_value () =
  Condition.condition "malformed-tuple-value"
    "A tuple field did not conform to what was expected" Condition.empty

let value_of_bencode = function
  | Encoding.Bencode.Tagged ('s', Encoding.Bencode.String s) -> Ok (Value.String s)
  | Encoding.Bencode.Tagged ('i', Encoding.Bencode.Int n) -> Ok (Value.Integer n)
  | _ -> Error (malformed_value ())

module rec Representation : (Encoding.Record.S with type t = t) = Encoding.Record.Make (Body)

and Body : Encoding.Record.BODY = struct
  type nonrec t = t

  let tag = 'T'
  let malformed = malformed_value

  let fields tuple =
    BatMap.foldi (fun name value acc -> (name, bencode_of_value value) :: acc) tuple []

  let of_fields data =
    let open Utilities.Result in
    let* kvs = Encoding.Bencode.as_dict data in
    kvs
    |> List.map (fun (name, v) -> value_of_bencode v |> Result.map (fun v -> (name, v)))
    |> Utilities.List.sequence
    |> Result.map (List.fold_left (fun acc (name, v) -> set name v acc) empty)
end

let hash tuple = Representation.to_blob tuple |> Hash.hash_of_blob