[2] | 1 | let version = "%%VERSION%%"
|
---|
| 2 |
|
---|
| 3 | open Cmdliner
|
---|
| 4 | open Logarion
|
---|
| 5 | module A = Logarion.Archive.Make(File_store)
|
---|
| 6 |
|
---|
| 7 | (* TODO: merge in lib/ so other modules can use (.e.g HTTP pull) *)
|
---|
| 8 | let 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 |
|
---|
| 33 | let 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 |
|
---|
| 43 | let 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 |
|
---|
| 60 | let 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 |
|
---|
| 65 | let 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 |
|
---|
| 69 | let 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 |
|
---|
| 81 | let 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 |
|
---|
| 87 | let 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 |
|
---|
| 94 | let 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 |
|
---|
| 100 | let init _force = File_store.init ()
|
---|
| 101 |
|
---|
| 102 | let 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 |
|
---|
| 108 | let 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 |
|
---|
| 133 | let 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 |
|
---|
| 138 | let cmds = [ init_term; new_term; file_term; unfile_term; list_term; last_term; Convert.term; Http.pull_term ]
|
---|
| 139 |
|
---|
| 140 | let () =
|
---|
| 141 | Random.self_init();
|
---|
| 142 | match Term.eval_choice default_cmd cmds with `Error _ -> exit 1 | _ -> exit 0
|
---|