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
open Forester_prelude
open Forester_core
open struct module T = Types end
module Item = struct
type color = Red | Green | Unknown
type meta = {
timestamp: float option;
color: color;
}
type t =
| Tree of URI.t
| Path of Trie.path
| Asset of string
let compare = compare
let hash = Hashtbl.hash
let equal = (=)
let check_timestamp
= fun path timestamp ->
match timestamp with
| Some timestamp ->
let last_modified = Eio.Path.(stat ~follow: true @@ path).mtime in
if last_modified > timestamp then
Red
else Green
| _ -> Red
end
module Dependency_tbl = Hashtbl.Make(Item)
module Dependecy_graph : sig
type t
type vertex = Item.t
val add_vertex : t -> vertex -> t
val create : ?size: int -> unit -> t
val pred : t -> vertex -> vertex list
val empty : unit -> t
end
= struct
module G = Graph.Imperative.Digraph.ConcreteBidirectional(Item)
type t = G.t
type vertex = Item.t
let create = G.create
let pred = G.pred
let add_vertex g v = G.add_vertex g v; g
let empty = G.create ?size: None
end
type t = {
tbl: Item.meta Dependency_tbl.t;
graph: Dependecy_graph.t;
db: Datalog_engine.db;
}
let empty = {
tbl = Dependency_tbl.create 1000;
graph = Dependecy_graph.create ();
db = Datalog_engine.db_create ();
}
let find_opt t uri = Dependency_tbl.find_opt t.tbl uri
let add_vertex t v color =
ignore @@ Dependecy_graph.add_vertex t.graph v;
Dependency_tbl.add t.tbl v color
let pred t v = Dependecy_graph.pred t.graph v
let get_changed_paths ~(config : Config.t) (cache : t) (dirs : Eio.Fs.dir_ty Eio.Path.t List.t) : Eio.Fs.dir_ty Eio.Path.t Seq.t =
let@ path = Seq.filter_map @~ Dir_scanner.scan_directories dirs in
let path_str = Eio.Path.native_exn path in
let uri = URI_scheme.path_to_uri ~base: config.url path_str in
let last_modified = Eio.Path.(stat ~follow: true path).mtime in
let (let*) o f = match o with None -> Some path | Some v -> f v in
let* {timestamp; _} = Dependency_tbl.find_opt cache.tbl (Tree uri) in
let* last_seen = timestamp in
if last_modified > last_seen then
Some path
else
None
let rec try_mark_green t node =
let exception Done of bool in
let dependencies =
let@ v = List.filter_map @~ pred t node in
match Dependency_tbl.find_opt t.tbl v with
| None -> None
| Some c ->
Some (v, c)
in
let result =
try
List.fold_right
(fun (dep, Item.{color; _}) acc ->
match color with
| Red -> raise (Done false)
| Green -> true && acc
| Unknown ->
if try_mark_green t dep then true && acc
else raise (Done false)
)
dependencies
true
with
| Done b -> b
in
if result then
Dependency_tbl.replace
t.tbl
node
{
color = Green;
timestamp = Some (Unix.time ())
}
else
assert false;
result
let marshal filename (v : t) =
let oc = open_out_bin filename in
Fun.protect
~finally: (fun () -> close_out oc)
(fun () -> Marshal.to_channel oc v [])
let unmarshal filename : t =
let ic = open_in_bin filename in
Fun.protect
~finally: (fun () -> close_in ic)
(fun () -> Marshal.from_channel ic)