source: code/trunk/lib/file_store.ml@ 8

Last change on this file since 8 was 6, checked in by fox, 3 years ago

txt publish <id>; file and convert to standard dirs

File size: 5.6 KB
Line 
1type t = string
2type item_t = t list
3type record_t = Text.t * item_t
4
5let extension = ".txt"
6let def_dir () =
7 let share = Sys.getenv "HOME" ^ "/.local/share/texts/" in
8 try Sys.getenv "txtdir" with Not_found ->
9 match Sys.is_directory share with
10 | true -> share
11 | false | exception (Sys_error _) -> "."
12
13let to_string f =
14 let ic = open_in f in
15 let s = really_input_string ic (in_channel_length ic) in
16 close_in ic;
17 s
18
19let fold_file_line fn init file = match open_in file with
20 | exception (Sys_error msg) -> prerr_endline msg; init
21 | file ->
22 let rec read acc = match input_line file with
23 | "" as s | s when String.get s 0 = '#' -> read acc
24 | s -> read (fn s acc)
25 | exception End_of_file -> close_in file; acc
26 in read init
27
28let file path str = let o = open_out path in output_string o str; close_out o
29
30let to_text path =
31 if Filename.extension path = extension then
32 (to_string path |> Text.of_string |> Result.map_error (fun m -> path ^": "^ m))
33 else Error (Printf.sprintf "Not txt: %s" path)
34
35let newest (a,_pa) (b,_pb) = Text.newest a b
36let oldest (a,_pa) (b,_pb) = Text.oldest a b
37
38let list_iter fn dir paths =
39 let link f = match to_text (Filename.concat dir f) with
40 | Ok t -> fn dir t f | Error s -> prerr_endline s in
41 List.iter link paths
42
43module TextMap = Map.Make(Text)
44
45type iteration_t = item_t TextMap.t
46let new_iteration = TextMap.empty
47
48(*let iter_valid_text pred fn path =*)
49(* match to_text path with Error _ -> () | Ok t -> if pred t then fn (t, p)*)
50
51let fold_valid_text pred it path =
52 match to_text path with Error _ -> it
53 | Ok t -> if pred t then (TextMap.update t
54 (function None -> Some [path] | Some ps -> Some (path::ps)) it
55 ) else it
56
57let split_filetypes files =
58 let acc (dirs, files) x = if Sys.is_directory x
59 then (x::dirs, files) else (dirs, x::files) in
60 List.fold_left acc ([],[]) files
61
62(* Compare file system nodes to skip reparsing? *)
63let list_fs ?(r=false) dir =
64 let valid_dir f = r && String.get f 0 <> '.' && Sys.is_directory f in
65 let expand_dir d = Array.(to_list @@ map (Filename.concat d) (Sys.readdir d)) in
66 let rec loop result = function
67 | f::fs when valid_dir f -> expand_dir f |> List.append fs |> loop result
68 | f::fs -> loop (f::result) fs
69 | [] -> result in
70 let dirs = if dir = "." then Array.to_list (Sys.readdir dir) else
71 if not r then expand_dir dir else [dir] in
72 loop [] dirs
73
74let list_take n =
75 let rec take acc n = function [] -> []
76 | x::_ when n = 1 -> x::acc
77 | x::xs -> take (x::acc) (n-1) xs
78 in take [] n
79
80let fold_sort_take ?(predicate=fun _ -> true) ?(number=None) comp flist =
81 (match number with None -> (fun x -> x) | Some n -> list_take n)
82 @@ List.fast_sort comp @@ TextMap.bindings
83 @@ List.fold_left (fold_valid_text predicate) new_iteration flist
84
85let iter ?(r=false) ?(dir=def_dir ()) ?(predicate=fun _ -> true) ?order ?number fn =
86 let flist = list_fs ~r dir in match order with
87 | Some comp -> List.iter fn @@ fold_sort_take ~predicate ~number comp flist
88 | None -> List.iter fn @@ TextMap.bindings @@
89 List.fold_left (fold_valid_text predicate) new_iteration flist
90
91let fold ?(r=false) ?(dir=def_dir ()) ?(predicate=fun _ -> true) ?order ?number fn acc =
92 let flist = list_fs ~r dir in match order with
93 | Some comp -> List.fold_left fn acc @@ fold_sort_take ~predicate ~number comp flist
94 | None -> List.fold_left fn acc @@ TextMap.bindings @@
95 List.fold_left (fold_valid_text predicate) new_iteration flist
96
97let with_id ?(r=false) ?(dir=def_dir ()) id =
98 let matched acc path =
99 match to_text path with
100 | Error x -> prerr_endline x; acc
101 | Ok text when text.Text.id <> id -> acc
102 | Ok text ->
103 match acc with
104 | Ok None -> Ok (Some text)
105 | Ok (Some prev) -> if prev = text then acc else Error [text; prev]
106 | Error x -> Error (text :: x)
107 in List.fold_left matched (Ok None) (list_fs ~r dir)
108
109let with_dir ?(descr="") ?(perm=0o740) dir =
110 let mkdir dir = match Unix.mkdir dir perm with
111 | exception Unix.Unix_error (EEXIST, _, _) -> ()
112 | exception Unix.Unix_error (code, _fn, arg) ->
113 failwith @@ Printf.sprintf "Error %s making %s dir: %s"
114 (Unix.error_message code) descr arg
115 | _ -> () in
116 let rec mkeach path = function [] | [""] -> () | ""::t -> mkeach path t
117 | hd::t -> let d = Filename.concat path hd in mkdir d; mkeach d t in
118 mkeach
119 (if Filename.is_relative dir then "" else "/")
120 (String.split_on_char '/' dir)
121
122let rec with_dirs = function [] -> () | (d, descr)::tl -> with_dir ~descr d; with_dirs tl
123
124let versioned_basename_of_title ?(version=0) repo extension (title : string) =
125 let basename = Text.string_alias title in
126 let rec next version =
127 let candidate = Filename.concat repo
128 (basename ^ "." ^ string_of_int version ^ extension) in
129 if Sys.file_exists candidate then next (succ version) else candidate
130 in
131 next version
132
133let id_filename repo extension text =
134 let basename = Text.alias text in
135 let candidate = Filename.concat repo (text.id ^ "." ^ basename ^ extension) in
136 if Sys.file_exists candidate then Error "Name clash, try again" else Ok candidate
137
138let with_text ?(dir=def_dir ()) new_text =
139 match id_filename dir extension new_text with
140 | Error _ as e -> e
141 | Ok path ->
142 try file path (Text.to_string new_text); Ok (path, new_text)
143 with Sys_error s -> Error s
144
145module Config = struct
146 type t = string Store.KV.t
147 let key_value k v a = Store.KV.add k (String.trim v) a
148end
149
150let of_kv_file path =
151 let open Text_parse in
152 let subsyntaxes = Parsers.Key_value.[|
153 (module Make (Config) : Parser.S with type t = Config.t); (module Make (Config)); |] in
154 let of_string text acc =
155 Parser.parse subsyntaxes { text; pos = 0; right_boundary = String.length text - 1 } acc in
156 of_string (to_string @@ path) Store.KV.empty
Note: See TracBrowser for help on using the repository browser.