aboutsummaryrefslogtreecommitdiffstats
path: root/lib/concepts/Relation.ml
blob: ef5ac12b11e71f2308848ec6962f4909952651fa (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
type address = Hash.hash
type schema = string BatMap.String.t

type t =
  { name: string;
    tuples: address;
    schema: schema }

module Field = struct
  let name = "name"
  let tuples = "tuples"
  let schema = "schema"
end

module Error = struct
  open Condition

  let malformed_relation () =
    condition "relation-malformed" "A relation representation did not conform to what was expected"
      empty

  let malformed_hash length =
    condition "relation-malformed-hash" "A relation tuple root was not a valid hash"
      ("length" |=| Value.Integer length)
end

let make ~name ~tuples ~schema = {name; tuples; schema}

let name relation = relation.name
let tuples relation = relation.tuples
let schema relation = relation.schema

let schema_empty = BatMap.String.empty

let schema_of_list fields =
  List.fold_left (fun acc (name, domain) -> BatMap.String.add name domain acc) schema_empty
    fields

let schema_to_list schema = BatMap.String.bindings schema

let schema_to_bencode schema =
  Codec.Bencode.Dict
    (schema_to_list schema
    |> List.map (fun (name, domain) -> name, Codec.Bencode.String domain) )

let schema_of_bencode value =
  let open Utilities.Result in
  let* fields = Codec.Bencode.as_dict value in
  fields
  |> List.map (fun (name, domain) ->
         let* domain = Codec.Bencode.as_string domain in
         Ok (name, domain) )
  |> Utilities.List.sequence
  |> Result.map schema_of_list

module Body = struct
  type nonrec t = t

  let tag = 'r'
  let malformed = Error.malformed_relation

  let equal left right =
    left.name = right.name
    && Hash.hash_equals left.tuples right.tuples
    && BatMap.String.equal String.equal left.schema right.schema

  let fields relation =
    [ Field.name, Object.Field.string relation.name;
      Field.schema, schema_to_bencode relation.schema;
      Field.tuples, Object.Field.address relation.tuples ]

  let of_fields data =
    let open Utilities.Result in
    let* name = Object.Field.require_string Field.name data in
    let* tuples = Object.Field.require_address ~malformed:Error.malformed_hash Field.tuples data in
    let* schema_data = Codec.Bencode.field Field.schema data in
    let* schema = schema_of_bencode schema_data in
    Ok {name; tuples; schema}
end

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