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

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

Samhain 21

Converter

  • type selection
  • subdir conversion
  • htm extension

Gemini

  • index.gmi
  • topics and latest
  • gmi.atom feed

Add pull (http(s)) operation

  • peers.pub.conf and peers.priv.conf

HTML5 format & fixes by Novaburst
Phony target (thanks Gergely)

May

Basic unit renamed from Note to Text.
New modular text-parser, internal to Logarion, for generic notation parsing. The default input format is now a much plainer text.
Logarion created texts have part of the UUID in filename.
Logarion's index re-written in Messagepack format. Removed indices command. They are generated during convert.

File size: 5.4 KB
Line 
1type t = string
2type item_t = string
3type archive_t = {
4 name: string; archivists: Person.Set.t; id: Id.t;
5 kv: string Store.KV.t; store: t }
6type record_t = Text.t * item_t
7
8let extension = ".txt"
9
10let to_string f =
11 let ic = open_in f in
12 let n = in_channel_length ic in
13 let s = Bytes.create n in
14 really_input ic s 0 n;
15 close_in ic;
16 Bytes.to_string s
17
18let file path content = let out = open_out path in
19 output_string out content; close_out out
20
21let (//) a b = a ^ "/" ^ b
22
23let to_text path =
24 if Filename.extension path = extension then
25 (to_string path |> Text.of_string |> Result.map_error (fun m -> path ^": "^ m))
26 else Error "Not txt"
27
28let newest (a,_pa) (b,_pb) = Text.newest a b
29let oldest (a,_pa) (b,_pb) = Text.oldest a b
30
31let list_iter fn {store;_} paths =
32 let link f = match to_text (Filename.concat store f)
33 with Ok t -> fn store t f | Error s -> prerr_endline s in
34 List.iter link paths
35
36let iter_valid_text pred fn p =
37 match to_text p with Error _ -> () | Ok t -> if pred t then fn (t, p)
38
39let fold_valid_text pred fn acc p =
40 match to_text p with Error _ -> acc | Ok t -> if pred t then fn acc (t, p) else acc
41
42let list_fs dir =
43 let rec loop result = function
44 | [] -> result
45 | f::fs when Sys.is_directory f ->
46 Array.map (Filename.concat f) (Sys.readdir f)
47 |> Array.to_list |> List.append fs |> loop result
48 | f::fs -> loop (f::result) fs
49 in loop [] [dir]
50
51let list_take n =
52 let rec take acc n = function [] -> []
53 | x::_ when n = 1 -> x::acc
54 | x::xs -> take (x::acc) (n-1) xs
55 in take [] n
56
57let iter ?(predicate=fun _ -> true) ?order ?number fn {store;_} =
58 match order with
59 | None -> List.iter (iter_valid_text predicate fn) @@ list_fs store
60 | Some comp ->
61 List.iter fn
62 @@ (match number with None -> (fun x -> x) | Some n -> list_take n)
63 @@ List.fast_sort comp
64 @@ List.fold_left (fold_valid_text predicate (fun a e -> List.cons e a)) []
65 @@ list_fs store
66
67let fold ?(predicate=fun _ -> true) ?order ?number fn acc {store;_} =
68 match order with
69 | None -> List.fold_left (fold_valid_text predicate fn) acc @@ list_fs store
70 | Some comp ->
71 List.fold_left fn acc
72 @@ (match number with None -> (fun x -> x) | Some n -> list_take n)
73 @@ List.fast_sort comp
74 @@ List.fold_left (fold_valid_text predicate (fun a e -> List.cons e a)) []
75 @@ list_fs store
76
77let with_id { store; _ } id =
78 let matched acc path =
79 match to_text path with
80 | Error x -> prerr_endline x; acc
81 | Ok text when text.Text.uuid <> id -> acc
82 | Ok text ->
83 match acc with
84 | Ok None -> Ok (Some text)
85 | Ok (Some prev) -> if prev = text then acc else Error [text; prev]
86 | Error x -> Error (text :: x)
87 in List.fold_left matched (Ok None) (list_fs store)
88
89module Directory = struct
90 let print ?(descr="") dir result =
91 let () = match result with
92 | Ok true -> print_endline ("Created " ^ descr ^ " directory " ^ dir)
93 | Ok false -> print_endline ("Using existing " ^ descr ^ " directory " ^ dir)
94 | Error (`Msg msg) -> prerr_endline @@ "Failed making " ^ descr ^ ". " ^ msg
95 in
96 result
97
98 let directory dir = Result.bind (Fpath.of_string dir) Bos.OS.Dir.create
99
100 let rec directories = function
101 | [] -> Ok ()
102 | (d, descr)::tl ->
103 match directory d |> print ~descr d with
104 | Ok _ -> directories tl
105 | Error _ -> Error (d, descr)
106end
107
108let copy ?(recursive = false) src dst =
109 Bos.OS.Cmd.run (Bos.Cmd.(v "cp" %% (on recursive @@ v "-r") % src % dst))
110
111let versioned_basename_of_title ?(version=0) repo extension (title : string) =
112 let basename = Text.string_alias title in
113 let rec next version =
114 let candidate = repo // basename ^ "." ^ string_of_int version ^ extension in
115 if Sys.file_exists candidate then next (succ version) else candidate
116 in
117 next version
118
119let uuid_filename repo extension text =
120 let basename = Text.alias text in
121 let candidate = repo // String.sub (Id.to_string text.uuid) 0 6 ^ "." ^ basename ^ extension in
122 if Sys.file_exists candidate then Error "Name clash, try again" else Ok candidate
123
124let with_text {store;_} new_text =
125 Result.bind (uuid_filename store extension new_text) @@
126 fun path ->
127 try file path (Text.to_string new_text); Ok (path, new_text) with Sys_error s -> Error s
128
129let basic_config () =
130 "Archive-Name: "
131 ^ "\nArchive-ID: " ^ Id.(generate () |> to_string)
132 ^ "\nArchivists: " ^ Bos.OS.Env.opt_var "USER" ~absent:""
133 |> Bytes.of_string
134
135let init ?(dotdir=".logarion/") () =
136 match Directory.directories [dotdir, "dotdir"] with
137 | Error (_dir, _desc) -> ()
138 | Ok () ->
139 let config_file =
140 open_out_gen [Open_creat; Open_excl; Open_wronly]
141 0o700 (dotdir // "config") in
142 output_bytes config_file (basic_config ());
143 close_out config_file
144
145module Config = struct
146 type t = archive_t
147 let key_value k v a = match k with
148 | "Archive-Name" -> { a with name = String.trim v }
149 | "Archive-ID" -> { a with id = Option.get (Id.of_string (String.trim v)) }
150 | "Archivists" -> { a with archivists = Person.Set.of_string v }
151 | _ -> { a with kv = Store.KV.add k (String.trim v) a.kv }
152end
153
154let of_path store =
155 let open Text_parse in
156 let subsyntaxes = [| (module Parsers.Key_value.Make (Config) : Parser.S with type t = Config.t); (module Parsers.Key_value.Make (Config)); |] in
157 let of_string text acc = Parser.parse subsyntaxes { text; pos = 0; right_boundary = String.length text - 1 } acc in
158 Ok (
159 of_string (to_string @@ store ^ "/.logarion/config") {
160 name = "";
161 archivists = Person.Set.empty;
162 id = Id.nil;
163 kv = Store.KV.empty;
164 store = try Sys.getenv "LOGARION_DIR" with Not_found -> "."
165 }
166 )
Note: See TracBrowser for help on using the repository browser.