source: code/trunk/cli/cli.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: 6.6 KB
Line 
1let version = "%%VERSION%%"
2
3open Cmdliner
4open Logarion
5module A = Logarion.Archive.Make(File_store)
6
7(* TODO: merge in lib/ so other modules can use (.e.g HTTP pull) *)
8let text_list order_opt reverse_opt number_opt values_opt authors_opt topics_opt =
9 match A.of_path (Sys.getcwd ()) with
10 | Error msg -> prerr_endline msg
11 | Ok archive ->
12 let predicates = A.predicate A.authored authors_opt @ A.predicate A.topics topics_opt in
13 let predicate text = List.fold_left (fun a e -> a && e text) true predicates in
14 let print_fold ~predicate fn =
15 let ts = A.fold ~predicate fn String_set.empty archive in
16 String_set.iter (print_endline) ts
17 in
18 let list_text (t, fname) = print_endline (Text.short_id t ^ " " ^ t.Text.title ^ "\t" ^ fname) in
19 match values_opt with
20 | Some "topics" -> print_fold ~predicate (fun a (e,_) -> (String_set.union a (Text.set "topics" e)))
21 | Some "authors" ->
22 let s = A.fold ~predicate (fun a (e,_) -> Person.Set.union a e.Text.authors) Person.Set.empty archive in
23 print_endline @@ Person.Set.to_string s
24 | Some x -> prerr_endline @@ "Unrecognised field: " ^ x
25 | None -> match order_opt with
26 | false -> A.iter ~predicate list_text archive
27 | true ->
28 let order = match reverse_opt with true -> A.newest | false -> A.oldest in
29 match number_opt with
30 | Some number -> A.iter ~predicate ~order ~number list_text archive
31 | None -> A.iter ~predicate ~order list_text archive
32
33let list_term =
34 let reverse = Arg.(value & flag & info ["r"] ~doc:"reverse order") in
35 let time = Arg.(value & flag & info ["t"] ~doc:"Sort by time, newest first") in
36 let number = Arg.(value & opt (some int) None & info ["n"] ~docv:"NUMBER" ~doc:"number of entries to list") in
37 let values = Arg.(value & opt (some string) None & info ["values"] ~docv:"HEADER-FIELD" ~doc:"unique values for header field") in
38 let authed = Arg.(value & opt (some string) None & info ["authored"] ~docv:"AUTHORS" ~doc:"texts by authors") in
39 let topics = Arg.(value & opt (some string) None & info ["topics"] ~docv:"TOPICS" ~doc:"texts with topics") in
40 Term.(const text_list $ time $ reverse $ number $ values $ authed $ topics),
41 Term.info "list" ~doc:"list texts" ~man:[ `S "DESCRIPTION"; `P "List texts" ]
42
43let print_last search_mine =
44 let last a ((t,_) as pair) = match a with None -> Some pair
45 | Some (t', _) as pair' -> if Text.newest t t' > 0 then Some pair else pair' in
46 match A.of_path (Sys.getcwd ()) with
47 | Error msg -> prerr_endline msg
48 | Ok archive ->
49 let last_mine a ((t,_) as pair) =
50 let open Text in
51 match a with None ->
52 if Person.Set.subset archive.A.archivists t.authors then Some pair else None
53 | Some (t', _) as pair' ->
54 if Text.newest t t' > 0 && Person.Set.subset archive.A.archivists t'.authors
55 then Some pair else pair'
56 in
57 match A.fold (if search_mine then last_mine else last) None archive with
58 | Some (_,f) -> print_endline f | None -> ()
59
60let last_term =
61 let mine = Arg.(value & flag & info ["mine"] ~doc:"last text authored by me") in
62 Term.(const print_last $ mine),
63 Term.info "last" ~doc:"most recent test" ~man:[ `S "DESCRIPTION"; `P "Print the filename of most recent text" ]
64
65let split_filetypes files =
66 let acc (dirs, files) x = if Sys.is_directory x then (x::dirs, files) else (dirs, x::files) in
67 List.fold_left acc ([],[]) files
68
69let file files = match A.of_path "." with
70 | Error msg -> prerr_endline msg
71 | Ok _archive ->
72 let dirs, files = split_filetypes files in
73 let _link_as_named dir file = Unix.link file (dir ^"/"^ file) in
74 let link_with_id dir file =
75 match File_store.to_text file with Error s -> prerr_endline s
76 | Ok t -> Unix.link file (dir ^"/"^ String.sub (Id.to_string (t.Text.uuid)) 0 8 ^".txt")
77 in
78 let link = link_with_id in
79 List.iter (fun d -> List.iter (link d) files) dirs
80
81let file_term =
82 let files = Arg.(value & pos_all string [] & info [] ~doc:"filenames") in
83 let doc = "file texts in directories" in
84 let man = [ `S "DESCRIPTION"; `P doc ] in
85 Term.(const file $ files), Term.info "file" ~doc ~man
86
87let unfile files = match A.of_path "." with
88 | Error msg -> prerr_endline msg
89 | Ok _archive ->
90 let dirs, files = split_filetypes files in
91 let unlink dir file = try Unix.unlink (dir ^"/"^ file) with Unix.(Unix_error(ENOENT,_,_))-> () in
92 List.iter (fun d -> List.iter (unlink d) files) dirs
93
94let unfile_term =
95 let files = Arg.(value & pos_all string [] & info [] ~doc:"filenames") in
96 let doc = "unfile texts from directories" in
97 let man = [ `S "DESCRIPTION"; `P doc ] in
98 Term.(const unfile $ files), Term.info "unfile" ~doc ~man
99
100let init _force = File_store.init ()
101
102let init_term =
103 let force = Arg.(value & flag & info ["f"; "force"] ~doc:"Initialise even if directory is not empty") in
104 let doc = "initialise a text repository in present directory" in
105 let man = [ `S "DESCRIPTION"; `P "Start an archive in current directory" ] in
106 Term.(const init $ force), Term.info "init" ~doc ~man
107
108let new_term =
109 let f title topics_opt interactive =
110 match A.of_path "." with
111 | Error m -> prerr_endline m
112 | Ok archive ->
113 let t = match title with "" -> "Draft" | _ -> title in
114 let authors = archive.archivists in
115 let date = Date.({ created = Some (Ptime_clock.now ()); edited = None }) in
116 let text = { (Text.blank ()) with title = t; authors; date } in
117 let text = try Text.with_str_set text "Topics" (Option.get topics_opt) with _ -> text in
118 match File_store.with_text archive text with
119 | Error s -> prerr_endline s
120 | Ok (filepath, _note) ->
121 match interactive with false -> print_endline filepath
122 | true ->
123 print_endline @@ "Created: " ^ filepath;
124 let _code = Sys.command ("$EDITOR " ^ filepath) in
125 ()
126 in
127 let title = Arg.(value & pos 0 string "" & info [] ~docv:"TITLE" ~doc:"Title for new article") in
128 let topics= Arg.(value & opt (some string) None & info ["t"; "topics"] ~docv:"TOPICS" ~doc:"Topics for new article") in
129 let inter = Arg.(value & flag & info ["i"; "interactive"] ~doc:"Prompts through the steps of creation and publication") in
130 let man = [ `S "DESCRIPTION"; `P "Create a new article, with title 'Draft' when none provided"] in
131 Term.(const f $ title $ topics $ inter), Term.info "new" ~doc:"create a new article" ~man
132
133let default_cmd =
134 let doc = "text archival & publishing" in
135 let man = [ `S "BUGS"; `P "Submit bugs <mailto:logarion@lists.orbitalfox.eu?subject=Issue: " ] in
136 Term.(ret (const (`Help (`Pager, None)))), Term.info "txt" ~version ~doc ~man
137
138let cmds = [ init_term; new_term; file_term; unfile_term; list_term; last_term; Convert.term; Http.pull_term ]
139
140let () =
141 Random.self_init();
142 match Term.eval_choice default_cmd cmds with `Error _ -> exit 1 | _ -> exit 0
Note: See TracBrowser for help on using the repository browser.