open Logarion let is_older source dest = try Unix.((stat dest).st_mtime < (stat source).st_mtime) with _-> true let convert cs r (text, files) = match Text.str "Content-Type" text with | "" | "text/plain" -> let source = List.hd files in let dest = Filename.concat r.Conversion.dir (Text.short_id text) in List.fold_left (fun a f -> let dest = dest ^ f.Conversion.ext in if is_older source dest then (File_store.file dest (f.Conversion.page r text); true) else false || a) false cs | x -> Printf.eprintf "Can't convert Content-Type: %s file: %s" x text.Text.title; false let converters types kv = let t = [] in let t = if ("htm" = types || "all" = types) then (let htm = Html.init kv in Conversion.{ ext = Html.ext; page = Html.page htm; indices = Html.indices htm })::t else t in let t = if ("gmi" = types || "all" = types) then Conversion.{ ext = Gemini.ext; page = Gemini.page; indices = Gemini.indices}::t else t in t let directory converters noindex dir id kv = let empty = Topic_set.Map.empty in let repo = Conversion.{ id; dir; kv; topic_roots = []; topics = empty; texts = [] } in let fn (ts,ls,acc) ((elt,_) as r) = (Topic_set.to_map ts (Text.set "topics" elt)), elt::ls, if convert converters repo r then acc+1 else acc in let topics, texts, count = File_store.(fold ~dir ~order:newest fn (empty,[],0)) in let topic_roots = try List.rev @@ String_set.list_of_csv (Store.KV.find "Topics" kv) with Not_found -> Topic_set.roots topics in let repo = Conversion.{ repo with topic_roots; topics; texts } in if not noindex then List.iter (fun c -> c.Conversion.indices repo) converters; Printf.printf "Converted: %d Indexed: %d\n" count (List.length texts) let at_path types noindex path = match path with "" -> prerr_endline "unspecified text file or directory" | dir when Sys.file_exists dir && Sys.is_directory dir -> let fname = Filename.concat dir "index.pck" in (match Header_pack.of_string @@ File_store.to_string fname with | Error s -> prerr_endline s | Ok { info; peers; _ } -> let kv = let f = Filename.concat dir ".convert.conf" in (* TODO: better place to store convert conf? *) if Sys.file_exists f then File_store.of_kv_file f else Store.KV.empty in let kv = if Store.KV.mem "Title" kv then kv else Store.KV.add "Title" info.Header_pack.title kv in let kv = Store.KV.add "Locations" (String.concat ";\n" info.Header_pack.locations) kv in let kv = Store.KV.add "Peers" (String.concat ";\n" Header_pack.(to_str_list peers)) kv in let cs = converters types kv in directory cs noindex dir info.Header_pack.id kv) | path when Sys.file_exists path -> let repo = Conversion.{ id = ""; dir = ""; kv = Store.KV.empty; topic_roots = []; topics = Topic_set.Map.empty; texts = [] } in let cs = converters types repo.kv in (match File_store.to_text path with | Ok text -> ignore @@ convert cs repo (text, [path]) | Error s -> prerr_endline s) | path -> Printf.eprintf "Path doesn't exist: %s" path open Cmdliner let term = let path = Arg.(value & pos 0 string "" & info [] ~docv:"path" ~doc:"Text file or directory to convert. Ff directory is provided, it must contain an index.pck (see: txt index)") in let types = Arg.(value & opt string "all" & info ["t"; "type"] ~docv:"output type" ~doc:"Convert to file type") in let noindex = Arg.(value & flag & info ["noindex"] ~doc:"Don't create indices in target format") in Term.(const at_path $ types $ noindex $ path), Term.info "convert" ~doc:"convert texts" ~man:[ `S "DESCRIPTION"; `P "Convert text or indexed texts within a directory to another format. If path is a directory must contain an index.pck. Run `txt index` first." ]