aboutsummaryrefslogblamecommitdiffstats
path: root/lib/concepts/Configuration.ml
blob: 5ca95d55bbadc4f4b7e84091b09200de09c5710f (plain) (tree)





































































                                                                                                   
open Sexplib.Sexp

type term = Sexplib.Sexp.t

module Error = struct
  open Condition

  let missing_key key =
    condition "missing-key" "An expected key was not found" ("key" |=| Value.String key)

  let mismatched_tag expected actual =
    condition "mismatched-tag" "A tag did not match what was expected"
      ("expected" |=| Value.String expected & "actual" |=| Value.String actual)

  let syntax_error form =
    condition "syntax-error" "Syntax error"
      ("form" |=| Value.String (Sexplib.Sexp.to_string_hum form))

  let tagless_form form =
    condition "tagless-form" "Cannot take tag of an atom or an empty list"
      ~parent:(syntax_error form) empty

  let bodyless_form form =
    condition "bodyless-form" "Cannot take body of an atom or an empty list"
      ~parent:(syntax_error form) empty

  let malformed_pair form =
    condition "malformed-pair" "Expected a pair of key and value" ~parent:(syntax_error form) empty

  let expected_atom form =
    condition "expected-atom" "Expected atom but got list structure" ~parent:(syntax_error form)
      empty

  let not_a_number form =
    condition "not-a-number" "Could not parse value as number" ~parent:(syntax_error form) empty
end

let term_of_sexp t = t
let tag_of = function List (Atom tag :: _) -> Ok tag | form -> Error (Error.tagless_form form)
let body_of = function List (_ :: body) -> Ok body | form -> Error (Error.bodyless_form form)

type dictionary = (string, term) BatMap.t

let value_for k ?default d =
  let default = Option.to_result ~none:(Error.missing_key k) default in
  BatMap.find_opt k d |> Option.map (fun x -> Ok x) |> Option.value ~default

let as_dictionary t tag : (dictionary, Condition.condition) result =
  let open Utilities.Result in
  let strip_tag (tag : string) (t : term) =
    let* tag' = tag_of t in
    if tag' = tag then body_of t else Error (Error.mismatched_tag tag tag')
  in
  let as_dictionary' pairs =
    List.fold_right
      (fun term dict ->
        let* dict = dict in
        match term with
        | List [Atom k; v] -> Ok (BatMap.add k v dict)
        | p -> Error (Error.malformed_pair p) )
      pairs (Ok BatMap.empty)
  in
  strip_tag tag t |> fmap as_dictionary'

let as_string = function Atom s -> Ok s | form -> Error (Error.expected_atom form)

let as_int f =
  let open Utilities.Result in
  let* s = as_string f in
  int_of_string_opt s |> Option.to_result ~none:(Error.not_a_number f)