mte/unikernel/duniverse/dune_/otherlibs/dune-rpc/private/registry.ml
2025-11-11 02:07:51 +01:00

216 lines
5.3 KiB
OCaml

open Import
module File = struct
type t =
{ path : string
; contents : string
}
end
let _pid_of_path path =
Filename.basename path
|> Filename.chop_suffix_opt ~suffix:".pid"
|> Option.bind ~f:Int.of_string
;;
module Dune = struct
module T = struct
type t =
{ root : string
; pid : int
; where : Where.t
}
let compare t { root; pid; where } =
let open Ordering.O in
let= () = Int.compare t.pid pid in
let= () = String.compare t.root root in
Where.compare t.where where
;;
let to_dyn { root; pid; where } =
let open Dyn in
record [ "root", string root; "pid", int pid; "where", Where.to_dyn where ]
;;
end
include T
module C = Comparable.Make (T)
module Set = C.Set
let create ~where ~root ~pid = { where; root; pid }
let root t = t.root
let where t = t.where
let pid t = t.pid
let filename dune = Printf.sprintf "%d.csexp" dune.pid
let sexp : t Conv.value =
let open Conv in
let to_ (where, root, pid) = { where; root; pid } in
let from { where; root; pid } = where, root, pid in
let where = field "where" (required Where.sexp) in
let root = field "root" (required string) in
let pid = field "pid" (required int) in
iso (record (three where root pid)) to_ from
;;
type error =
| Of_sexp of Conv.error
| Csexp of
{ position : int
; message : string
}
exception E of error
let () =
Printexc.register_printer (function
| E (Of_sexp e) -> Some (Dyn.to_string (Conv.dyn_of_error e))
| E (Csexp { position; message }) ->
Some
(Dyn.to_string
(let open Dyn in
record [ "message", string message; "position", int position ]))
| _ -> None)
;;
let of_file (f : File.t) =
match Csexp.parse_string f.contents with
| Error (position, message) -> Error (Csexp { position; message })
| Ok s ->
(match Conv.of_sexp sexp ~version:(0, 0) s with
| Ok s -> Ok s
| Error e -> Error (Of_sexp e))
;;
end
module Config = struct
type t = Xdg.t
let watch_dir t =
let dir =
match Xdg.runtime_dir t with
| Some runtime -> runtime
| None -> Xdg.data_dir t
in
Filename.concat dir "dune/rpc"
;;
let create x = x
let register t dune =
let file =
let contents = Conv.to_sexp Dune.sexp dune |> Csexp.to_string in
let path = Filename.concat (watch_dir t) (Dune.filename dune) in
{ File.contents; path }
in
`Caller_should_write file
;;
end
type nonrec t =
{ config : Config.t
; mutable last_mtime : float option
; mutable current : Dune.t list
}
let create config = { config; last_mtime = None; current = [] }
let current t = t.current
module Refresh = struct
type t =
{ added : Dune.t list
; removed : Dune.t list
; errored : (string * exn) list
}
let empty = { added = []; removed = []; errored = [] }
let added t = t.added
let removed t = t.removed
let errored t = t.errored
end
module Poll
(Fiber : sig
type 'a t
val return : 'a -> 'a t
val parallel_map : 'a list -> f:('a -> 'b t) -> 'b list t
module O : sig
val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
end
end)
(IO : sig
val scandir : string -> (string list, exn) result Fiber.t
val stat : string -> ([ `Mtime of float ], exn) result Fiber.t
val read_file : string -> (string, exn) result Fiber.t
end) =
struct
open Fiber.O
let ( let** ) x f =
let* x = x in
match x with
| Ok s -> f s
| Error e -> Fiber.return (Error e)
;;
let ( let++ ) x f =
let+ x = x in
match x with
| Ok s -> Ok (f s)
| Error e -> Error e
;;
let poll t =
let dir = Config.watch_dir t.config in
let** (`Mtime mtime) = IO.stat dir in
let skip =
match t.last_mtime with
| Some last_mtime -> last_mtime >= mtime
| None ->
t.last_mtime <- Some mtime;
false
in
if skip
then Fiber.return (Ok Refresh.empty)
else
let++ results =
let** contents = IO.scandir dir in
let contents =
List.filter contents ~f:(fun fname -> fname <> "." && fname <> "..")
in
let+ res =
Fiber.parallel_map contents ~f:(fun fname ->
let path = Filename.concat dir fname in
let+ contents = IO.read_file path in
( path
, match contents with
| Error e -> Error e
| Ok contents ->
let file = { File.contents; path } in
(match Dune.of_file file with
| Ok _ as s -> s
| Error e -> Error (Dune.E e)) ))
in
Ok res
in
let new_current, errored =
List.partition_map results ~f:(fun (file, res) ->
match res with
| Ok s -> Left s
| Error e -> Right (file, e))
in
let current = t.current in
t.current <- new_current;
let module Set = Dune.Set in
let new_current = Set.of_list new_current in
let current = Set.of_list current in
{ Refresh.added = Set.to_list (Set.diff new_current current)
; removed = Set.to_list (Set.diff current new_current)
; errored
}
;;
end