summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarius Peter <marius.peter@tutanota.com>2025-05-01 19:28:20 +0200
committerMarius Peter <marius.peter@tutanota.com>2025-05-01 19:28:20 +0200
commitf94d27e3df990d9dd7dae69191dd05e5f691b0bb (patch)
tree0306d92ed67b1b9ad10aa1f7926a25daea119ee5
parentad02d028ab875c5d5ba90f3f8bed31f2678ed664 (diff)
Perform deep refactoring.
Better separation of concern; this paves the way for better testing down the road.
-rw-r--r--lib/git_helpers.ml34
-rw-r--r--lib/git_unhelpers.ml46
-rw-r--r--lib/handlers.ml37
-rw-r--r--lib/views.ml197
4 files changed, 112 insertions, 202 deletions
diff --git a/lib/git_helpers.ml b/lib/git_helpers.ml
index 0626d02..4e2215e 100644
--- a/lib/git_helpers.ml
+++ b/lib/git_helpers.ml
@@ -1,11 +1,39 @@
open Lwt.Infix
-let get_head_commit_hash repo_path =
- let full_path = Filename.concat Config.git_directory repo_path in
- let%lwt store_result = Git_unix.Store.v @@ Fpath.v full_path in
+let full_path path = Filename.concat Config.git_directory path
+
+let head_commit_hash repo_path =
+ let%lwt store_result = Git_unix.Store.v @@ Fpath.v @@ full_path repo_path in
match store_result with
| Error _ -> Lwt.return_error "Could not open the Git repository."
| Ok store -> (
Git_unix.Store.Ref.resolve store Git.Reference.head >|= function
| Error _ -> Error ("Failed to resolve HEAD for repo " ^ repo_path)
| Ok hash -> Ok (Git_unix.Store.Hash.to_hex hash))
+
+let latest_commits repo_path count =
+ let cmd =
+ Printf.sprintf "git -C %s log --pretty=format:'%%ad %%s' --date=short -n %d"
+ (full_path repo_path) count
+ in
+ Lwt.catch
+ (fun () ->
+ let%lwt output = Lwt_process.pread ("", [| "sh"; "-c"; cmd |]) in
+ let lines = String.split_on_char '\n' output in
+ Lwt.return_ok lines)
+ (fun exn -> Lwt.return_error (Printexc.to_string exn))
+
+let all_branches repo_path =
+ let cmd =
+ Printf.sprintf "git -C %s branch --format=%%(refname:short)"
+ @@ full_path repo_path
+ in
+ Lwt.catch
+ (fun () ->
+ let%lwt output = Lwt_process.pread ("", [| "sh"; "-c"; cmd |]) in
+ let branches =
+ String.split_on_char '\n' output
+ |> List.filter (fun s -> String.trim s <> "")
+ in
+ Lwt.return_ok branches)
+ (fun exn -> Lwt.return_error (Printexc.to_string exn))
diff --git a/lib/git_unhelpers.ml b/lib/git_unhelpers.ml
deleted file mode 100644
index 5b55c16..0000000
--- a/lib/git_unhelpers.ml
+++ /dev/null
@@ -1,46 +0,0 @@
-(* These will be reimplemented using OCaml's Git library, one day... *)
-
-let get_latest_commits repo_path count =
- let open Printf in
- let full_path = Filename.concat Config.git_directory repo_path in
- let full_cmd =
- let command = sprintf "git -C %s log" full_path in
- let options =
- let format = "--pretty=format:'%ad %s'" in
- let date = "--date=short" in
- let count = sprintf "-n %s" (string_of_int count) in
- [ format; date; count ]
- in
- String.concat " " (command :: options)
- in
- let ic = Unix.open_process_in full_cmd in
- let rec read_lines acc =
- try
- let line = input_line ic in
- read_lines (line :: acc)
- with End_of_file ->
- ignore (Unix.close_process_in ic);
- List.rev acc
- in
- read_lines []
-
-let get_all_branches repo_path =
- let open Printf in
- let full_path = Filename.concat Config.git_directory repo_path in
- let full_cmd = sprintf "git -C %s branch" full_path in
- let ic = Unix.open_process_in full_cmd in
- let rec read_lines acc =
- try
- let line = input_line ic |> String.trim in
- let clean_line =
- if String.length line > 2 && String.sub line 0 2 = "* " then
- String.sub line 2 (String.length line - 2)
- (* Remove "* " from active branch *)
- else line
- in
- read_lines (clean_line :: acc)
- with End_of_file ->
- ignore (Unix.close_process_in ic);
- List.rev acc
- in
- read_lines []
diff --git a/lib/handlers.ml b/lib/handlers.ml
index 171cef2..27ca0b6 100644
--- a/lib/handlers.ml
+++ b/lib/handlers.ml
@@ -1,27 +1,34 @@
-let ogit_root _req = Views.Ogit_root.render () |> Dream_html.respond
+let ogit_root _req = Views.ogit_root () |> Dream_html.respond
-let repo_root req =
- let repo_name = Dream.param req "repo_name" in
- Views.Repo_root.render repo_name |> Dream_html.respond
+let repo_summary req =
+ let repo_path = Dream.param req "repo_name" in
+ let%lwt branches_result = Git_helpers.all_branches repo_path in
+ let%lwt commits_result = Git_helpers.latest_commits repo_path 10 in
+ match (branches_result, commits_result) with
+ | Ok branches, Ok commits ->
+ Views.repo_summary repo_path ~branches ~commits |> Dream_html.respond
+ | Error msg, _ | _, Error msg -> Views.error_page msg |> Dream_html.respond
-let repo_tree req =
- let repo_name = Dream.param req "repo_name" in
- let dir_path = Dream.target req in
- Views.Repo_tree.render repo_name dir_path |> Dream_html.respond
+(* let repo_tree req = *)
+(* let repo_name = Dream.param req "repo_name" in *)
+(* let path = Git_helpers.full_path repo_name in *)
+(* let dir_path = Dream.target req in *)
+(* Views.repo_tree ~repo_path:path dir_path |> Dream_html.respond *)
-let repo_blob req =
- let repo_name = Dream.param req "repo_name" in
- let blob_path = Dream.query req "path" |> Option.value ~default:"" in
- Views.Repo_blob.render repo_name blob_path |> Dream_html.respond
+(* let repo_blob req = *)
+(* let repo_name = Dream.param req "repo_name" in *)
+(* let path = Git_helpers.full_path repo_name in *)
+(* let blob_path = Dream.query req "path" |> Option.value ~default:"" in *)
+(* Views.repo_blob repo_name blob_path |> Dream_html.respond *)
let all_handlers =
[
Dream.get "/" ogit_root;
Dream.scope "/:repo_name" []
[
- Dream.get "/" repo_root;
- Dream.get "/tree" repo_tree;
- Dream.get "/blob" repo_blob;
+ Dream.get "/" repo_summary;
+ (* Dream.get "/tree" repo_tree; *)
+ (* Dream.get "/blob" repo_blob; *)
];
Dream.get "/static/**" (Dream.static "./lib/static");
]
diff --git a/lib/views.ml b/lib/views.ml
index bbb9de5..a362e40 100644
--- a/lib/views.ml
+++ b/lib/views.ml
@@ -42,11 +42,11 @@ module Layout = struct
]
end
-module Topnav = struct
+module Components = struct
open Dream_html
open HTML
- let repo repo_path current_path =
+ let topnav repo_path current_path =
let li_of_a (path, text) =
let is_active = path = current_path in
let attrs = if is_active then [ id "active" ] else [] in
@@ -72,19 +72,19 @@ module Topnav = struct
]
end
-module Ogit_root = struct
- open Dream_html
- open HTML
-
+let ogit_root () =
+ let open Dream_html in
+ let open HTML in
let repositories_in directory =
try
let repos =
Sys.readdir directory |> Array.to_list |> List.sort String.compare
in
- let li_of_repo repo = li [] [ a [ href "%s" repo ] [ txt "%s" repo ] ] in
+ let li_of_repo repo = li [] [ a [ href "%s/" repo ] [ txt "%s" repo ] ] in
div [ id "repositories" ] [ ul [] @@ List.map li_of_repo repos ]
with Sys_error _ ->
div [] [ txt "Error: Unable to read repository list." ]
+ in
let body_data =
{
@@ -93,134 +93,55 @@ module Ogit_root = struct
topnav = null [];
content = [ repositories_in Config.git_directory ];
}
-
- let render () = Layout.application body_data
-end
-
-module Repo_root = struct
- open Dream_html
- open HTML
-
- let branches =
- let repo_path = Fpath.v "/home/blendux/git.test/ogit.git/" in
- let load_repo () = Git_unix.Store.v ~dotgit:repo_path repo_path in
- let store = Lwt_main.run @@ load_repo () in
- let refs = Lwt_main.run @@ Git_unix.Store.Ref.list @@ Result.get_ok store in
- refs |> List.map fst |> List.map Git.Reference.to_string
-
- let render repo_path =
- let title = repo_path in
- let subtitle = Filename.concat Config.git_directory repo_path in
- let li_of_branch hash = li [] [ txt "%s" hash ] in
- let recent_commits = Git_unhelpers.get_latest_commits repo_path 10 in
- let li_of_commit commit =
- li [] [ a [ href "%s" commit ] [ txt "%s" commit ] ]
- in
- let content =
- [
- h3 [] [ txt "Branches" ];
- ul [] (List.map li_of_branch branches);
- h3 [] [ txt "Recent commits" ];
- ul [] (List.map li_of_commit recent_commits);
- ]
- in
- let body_data =
- { title; subtitle; topnav = Topnav.repo repo_path ""; content }
- in
- Layout.application body_data
-end
-
-module Repo_tree = struct
- open Dream_html
- open HTML
-
- let full_path repo_name dir_path =
- Filename.concat (Filename.concat Config.git_directory repo_name) dir_path
-
- (* Helper function to list contents of a given directory *)
- let ls_dir repo_name dir_path =
- let dir_full_path = full_path repo_name dir_path in
- try
- Sys.readdir dir_full_path |> Array.to_list
- |> List.filter (fun name -> name <> ".git") (* Exclude .git *)
- |> List.map (fun entry ->
- let entry_rel_path = Filename.concat dir_path entry in
- let full_entry_path = Filename.concat dir_full_path entry in
- (entry, entry_rel_path, Sys.is_directory full_entry_path))
- |> List.sort (fun (a, _, is_dir_a) (b, _, is_dir_b) ->
- match (is_dir_a, is_dir_b) with
- | true, false -> -1 (* Directories first *)
- | false, true -> 1
- | _ -> String.compare a b)
- with Sys_error _ -> []
-
- (* Function to create a link based on file type *)
- let link_for_entry repo_name (entry, entry_rel_path, is_dir) =
- let link =
- if is_dir then Printf.sprintf "/%s/tree?path=%s" repo_name entry_rel_path
- else Printf.sprintf "/%s/blob?path=%s" repo_name entry_rel_path
- in
- let display_name = if is_dir then entry ^ "/" else entry in
- li [] [ a [ href "%s" link ] [ txt "%s" display_name ] ]
-
- (* Render function *)
- let render repo_name dir_path =
- let title = repo_name in
- let subtitle = "Files" in
-
- let repo_entries = ls_dir repo_name @@ dir_path in
- let content =
- [
- txt "%s" dir_path;
- ul [] (List.map (link_for_entry repo_name) repo_entries);
- ]
- in
-
- let body_data =
- { title; subtitle; topnav = Topnav.repo repo_name "tree"; content }
- in
- Layout.application body_data
-end
-
-module Repo_blob = struct
- open Dream_html
- open HTML
-
- let full_path repo_path = Filename.concat Config.git_directory repo_path
-
- (* Read the contents of a file *)
- let read_blob repo_path blob_name =
- let file_path = Filename.concat (full_path repo_path) blob_name in
- try Some (In_channel.with_open_text file_path In_channel.input_all)
- with _ ->
- None (* Handle cases where the file doesn't exist or can't be read *)
-
- (* Render function *)
- let render repo_path blob_name =
- let title = blob_name in
- let subtitle = "File Contents" in
-
- match read_blob repo_path blob_name with
- | Some content ->
- let content_display = pre [] [ code [] [ txt "%s" content ] ] in
- let body_data =
- {
- title;
- subtitle;
- topnav = Topnav.repo repo_path "blob";
- content = [ content_display ];
- }
- in
- Layout.application body_data
- | None ->
- let error_message = p [] [ txt "Error: Unable to read file." ] in
- let body_data =
- {
- title;
- subtitle;
- topnav = Topnav.repo repo_path "blob";
- content = [ error_message ];
- }
- in
- Layout.application body_data
-end
+ in
+ Layout.application body_data
+
+let repo_summary repo_path ~branches ~commits =
+ let open Dream_html in
+ let open HTML in
+ let li_of_branch branch =
+ li [] [ a [ href "%s" branch ] [ txt "%s" branch ] ]
+ in
+ let li_of_commit commit =
+ li [] [ a [ href "%s" commit ] [ txt "%s" commit ] ]
+ in
+ let content =
+ [
+ h3 [] [ txt "Branches" ];
+ ul [] (List.map li_of_branch branches);
+ h3 [] [ txt "Recent commits" ];
+ ul [] (List.map li_of_commit commits);
+ ]
+ in
+ let body_data =
+ {
+ title = repo_path;
+ subtitle = "Macaroniii";
+ topnav = Components.topnav repo_path "";
+ content;
+ }
+ in
+ Layout.application body_data
+
+let error_page message =
+ let open Dream_html in
+ let open HTML in
+ html []
+ [
+ head []
+ [
+ title [] "Big error";
+ link [ rel "stylesheet"; href "/static/styles.css" ];
+ link [ rel "icon"; type_ "image/x-icon"; href "/static/git_icon.svg" ];
+ ];
+ body []
+ [
+ h1 [] [ txt "Major error alert" ];
+ h2 [] [ txt "Major alert subtitle" ];
+ (* Components.topnav; *)
+ div [ id "main" ] [ txt "%s" message ];
+ ];
+ ]
+
+let repo_tree repo_path = repo_summary repo_path
+let repo_blob repo_path = repo_summary repo_path
Copyright 2019--2025 Marius PETER