package owl-zoo

  1. Overview
  2. Docs

Source file owl_zoo_ver.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
161
162
163
164
165
166
167
168
169
170
171
172
173
(*
 * OWL - OCaml Scientific and Engineering Computing
 * Copyright (c) 2016-2019 Liang Wang <liang.wang@cl.cam.ac.uk>
 *)


let htb = Owl_zoo_path.htb


(** run system command and return the result as string *)
let syscall cmd =
  let ic, oc = Unix.open_process cmd in
  let buf = Buffer.create 50 in
  (
    try
      while true do
        Buffer.add_channel buf ic 1
      done
    with End_of_file -> ()
  );
  Unix.close_process (ic, oc) |> ignore;
  Buffer.contents buf


(** 
Version information of gists is saved as key-value pairs in Hash table:
key: gid; value: (version list, timestamp). 
*)
let create_htb () =
  let tb = Hashtbl.create 128 in
  Hashtbl.add tb "" ([|""|], 0.);
  Owl_io.marshal_to_file tb htb


(** 
Try to get the value of key `gid`; if not found, return default values with
a `true` flag. 
*)
let get_value (gid : string) =
  if not (Sys.file_exists htb) then create_htb ();
  let tb = Owl_io.marshal_from_file htb in
  if (gid = "") then ([|""|], 0., tb, true)
  else try
    let v, ts = Hashtbl.find tb gid in
    v, ts, tb, false
  with Not_found ->  [|""|], 0., tb, true


(** Get the timestamp of the latest version of a gist; return 0. if the gist
does not exist. *)
let get_timestamp (gid : string) =
  let _, ts, _, miss_flag = get_value gid in
  if miss_flag = false then ts else 0.


(** 
Get the most up-to-date gist version from Gist server; return "" if the gid
is not found or network error happens. 
*)
let get_remote_vid (gid : string) =
  let cmd = "curl https://api.github.com/gists/" ^ gid in
  let s = syscall cmd in
  let r = Str.regexp "\"version\":[ \n\r\t]+\"\\([0-9a-z]+\\)\"" in
  try (
    Str.search_forward r s 0 |> ignore;
    Str.matched_group 1 s
  )
  with Not_found -> ""


(** Get the latest version downloaded on local machine; if the local version is
not found on record, get the newest vid from Gist server. *)
let get_latest_vid (gid : string) (tol : float) =
  let v, ts, tb, miss_flag = get_value gid in
  let t = Unix.time () in
  if miss_flag = false && (t -. ts) < tol then (
    assert (Array.length v > 0);
    Array.get v (Array.length v - 1)
  ) else (
    Owl_log.debug "owl-zoo: Gist %s exceeds time tolerence %f; fetching newest vid from server" gid tol;
    Hashtbl.replace tb gid (v, t);
    Owl_io.marshal_to_file tb htb;
    get_remote_vid gid
  )


(** Check if a specific version of a gist exists on the record. *)
let exist (gid : string) (vid : string) =
  let v, _, _, miss_flag = get_value gid in
  if miss_flag = false then
    Array.mem vid v
  else false


(** 
Add a specific version of a gist to the record; if this version already
exists, do nothing; if this gist does not exist, create a new item. 
*)
let update (gid : string) (vid : string) =
  let v, _, tb, miss_flag = get_value gid in
  if (miss_flag = false) then (
    if not (Array.mem vid v) then (
      let v' = Array.append v [|vid|] in
      let ts = Unix.time () in
      Hashtbl.replace tb gid (v', ts);
      Owl_io.marshal_to_file tb htb;
    ) else (
      Owl_log.debug "owl-zoo: Gist %s/%s already exists in the record" gid vid
    )
  ) else (
    let v = [|vid|] in
    let ts = Unix.time () in
    Hashtbl.add tb gid (v, ts);
    Owl_io.marshal_to_file tb htb;
  )


(** Remove a gist's record. *)
let remove (gid : string)  =
  let _, _, tb, miss_flag = get_value gid in
  if miss_flag = false then (
    Hashtbl.remove tb gid;
    Owl_io.marshal_to_file tb htb;
  )
  else (
    Owl_log.debug "owl-zoo: Gist %s not found in the record" gid
  )


(* TODO: richer time format *)
let to_timestamp time_str =
  try float_of_string time_str
  with Failure _ -> raise Owl_exception.ZOO_ILLEGAL_GIST_NAME


(** 
Parse a full gist name scheme string and return a gist id, a version id, a
float value indicating the time tolerance for this gist, and a bool
flag indicating if `pin` is set to true in the gist name. 
*)
let parse_gist_string gist =
  let validate_len s len =
    if ((String.length s) = len) then s
    else raise Owl_exception.ZOO_ILLEGAL_GIST_NAME
  in
  let split ?(delim=" ") str =
    let regex = Str.regexp delim in
    Str.split_delim regex str
  in

  let lst = split ~delim:"?" ("gid=" ^ gist) in
  let params = Hashtbl.create 5 in
  List.iter (fun p ->
    let kv = split ~delim:"=" p |> Array.of_list in
    Hashtbl.add params kv.(0) kv.(1)
  ) lst;

  let gid = Hashtbl.find params "gid" in
  let tol =
    try Hashtbl.find params "tol" |> to_timestamp
    with Not_found -> infinity
  in
  let vid =
    try Hashtbl.find params "vid"
    with Not_found -> get_latest_vid gid tol
  in
  let pin =
    try Hashtbl.find params "pin" |> bool_of_string with
    | Not_found          -> false
    | Invalid_argument _ -> raise Owl_exception.ZOO_ILLEGAL_GIST_NAME
  in

  (validate_len gid 32), (validate_len vid 40), tol, pin
OCaml

Innovation. Community. Security.