package osnap

  1. Overview
  2. Docs

Source file snapshot.ml

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
(*****************************************************************************)
(* Open Source License                                                       *)
(* Copyright (c) 2021 Valentin Chaboche                                      *)
(* Copyright (c) 2021 Nomadic Labs, <contact@nomadic-labs.com>               *)
(*                                                                           *)
(* Permission is hereby granted, free of charge, to any person obtaining a   *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense,  *)
(* and/or sell copies of the Software, and to permit persons to whom the     *)
(* Software is furnished to do so, subject to the following conditions:      *)
(*                                                                           *)
(* The above copyright notice and this permission notice shall be included   *)
(* in all copies or substantial portions of the Software.                    *)
(*                                                                           *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER       *)
(* DEALINGS IN THE SOFTWARE.                                                 *)
(*                                                                           *)
(*****************************************************************************)

type ('fn, 'r) t =
  | Snapshot : {
      name : string;
      scenarios : ('fn, 'r) Scenario.t list;
    }
      -> ('fn, 'r) t

type mode = Marshal | Data_encoding

let pp_scenarios spec fmt scenarios =
  let pp fmt scenario =
    Format.pp_print_char fmt '\t' ;
    Scenario.pp fmt spec scenario
  in

  Format.pp_print_list ~pp_sep:Format.pp_print_newline pp fmt scenarios

let pp fmt spec (Snapshot { name; scenarios }) =
  Format.fprintf
    fmt
    "{@.  name = %s;@.  scenarios = [@.@[<hov 2>%a@]@.  ]@.}"
    name
    (pp_scenarios spec)
    scenarios

let to_string spec snapshot =
  let pp fmt = pp fmt spec in
  Format.asprintf "%a" pp snapshot

let encoding : type fn r. (fn, r) Spec.t -> (fn, r) t Data_encoding.encoding =
 fun spec ->
  let open Data_encoding in
  conv
    (fun (Snapshot { name; scenarios }) -> (name, scenarios))
    (fun (name, scenarios) -> Snapshot { name; scenarios })
    (obj2
       (req "name" string)
       (req "scenarios" @@ list @@ Scenario.encoding_scenario spec))

let create ~rand ~name ~spec ~f n =
  let scenarios =
    List.init n (fun _ -> Scenario.spec_to_scenario ~rand spec f)
  in
  Snapshot { name; scenarios }

let create_from_snapshot (Snapshot { name; scenarios }) f =
  let scenarios =
    List.map (fun scenario -> Scenario.reapply scenario f) scenarios
  in
  Snapshot { name; scenarios }

let encode ?spec ~mode ~path snapshot =
  let buf =
    match mode with
    | Marshal -> Marshal.to_string snapshot []
    | Data_encoding ->
        if Option.is_none spec then
          raise
            (Invalid_argument
               "Cannot encode a snapshot without the specification")
        else
          let spec = Option.get spec in
          if Spec.can_encode spec then
            let json = Data_encoding.Json.construct (encoding spec) snapshot in
            Data_encoding.Json.to_string json
          else
            raise
              (Invalid_argument
                 "Some encoding fields in the specification are missing")
  in
  Common.write path buf

exception SnapshotNotFound of string

exception DataEncodingError of string

exception DataEncodingMissing

exception MarshalError of string

let decode ?spec ~mode ~path () =
  if Sys.file_exists path then
    match mode with
    | Marshal -> (
        try
          let ic = open_in path in
          let x = Marshal.from_channel ic in
          let () = close_in ic in
          x
        with Failure s -> raise (MarshalError s))
    | Data_encoding -> (
        if Option.is_none spec then
          raise
            (Invalid_argument
               "Cannot decode a snapshot without the specification")
        else
          let spec = Option.get spec in
          let () =
            if not (Spec.can_encode spec) then raise DataEncodingMissing
          in
          let json = Common.read_file path |> Data_encoding.Json.from_string in
          let json =
            match json with
            | Ok x -> x
            | Error er -> raise (DataEncodingError er)
          in
          try Data_encoding.Json.destruct (encoding spec) json
          with e -> raise (DataEncodingError (Printexc.to_string e)))
  else raise (SnapshotNotFound (path ^ " does not exists"))

let decode_opt ?spec ~mode ~path () =
  try decode ?spec ~mode ~path () |> Option.some with
  | SnapshotNotFound _ -> None
  | DataEncodingError s ->
      let () =
        Printf.printf
          "Error: snapshot at %s could not be decoded using Data_encoding:\n\
           \t%s"
          path
          s
      in
      exit 1
  | MarshalError s ->
      let () =
        Printf.printf
          "Error: snapshot at %s could not be decoded using Marshal:\n\t%s"
          path
          s
      in
      exit 1
  | DataEncodingMissing ->
      let () =
        Printf.printf "Error: encoding are missing in the specification"
      in
      exit 1