diff options
author | Marius Peter <marius.peter@tutanota.com> | 2025-03-02 19:23:19 +0100 |
---|---|---|
committer | Marius Peter <marius.peter@tutanota.com> | 2025-03-02 19:23:19 +0100 |
commit | 55993c4d0857aec8a372aee989aaeef61ea37b3c (patch) | |
tree | a6da37b5fda45d80f84251633426dee51db87575 | |
parent | c67581470cbdf2a1c8efabce78afffb2e3a2a2c6 (diff) |
Great work today! 🔥
-rw-r--r-- | bin/main.ml | 3 | ||||
-rw-r--r-- | lib/git_unhelpers.ml | 35 | ||||
-rw-r--r-- | lib/handlers.ml | 7 | ||||
-rw-r--r-- | lib/views.ml | 145 |
4 files changed, 146 insertions, 44 deletions
diff --git a/bin/main.ml b/bin/main.ml index c30e4fc..74712e1 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -6,5 +6,8 @@ let () = Dream.get "/:repo_name" (fun req -> let repo_name = Dream.param req "repo_name" in Ogit.Handlers.repo_root repo_name); + Dream.get "/:repo_name/tree" (fun req -> + let repo_name = Dream.param req "repo_name" in + Ogit.Handlers.repo_tree repo_name); Dream.get "/static/**" (Dream.static "./lib/static"); ] diff --git a/lib/git_unhelpers.ml b/lib/git_unhelpers.ml index 28d4d3a..5b55c16 100644 --- a/lib/git_unhelpers.ml +++ b/lib/git_unhelpers.ml @@ -1,11 +1,17 @@ (* These will be reimplemented using OCaml's Git library, one day... *) -let get_git_log repo_path = +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 cmd = Printf.sprintf "git -C %s log" full_path in - let options = [ "--pretty=format:'%ad %s'"; "--date=short"; "-n 10" ] in - String.concat " " (cmd :: options) + 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 = @@ -17,3 +23,24 @@ let get_git_log repo_path = 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 4a6e0ca..46df354 100644 --- a/lib/handlers.ml +++ b/lib/handlers.ml @@ -1,6 +1,3 @@ -open Lwt.Infix - let ogit_root _ = Views.Ogit_root.render () |> Dream_html.respond - -let repo_root repo_name = - Views.Repo_root.render repo_name >>= Dream_html.respond +let repo_root repo_name = Views.Repo_root.render repo_name |> Dream_html.respond +let repo_tree repo_name = Views.Repo_tree.render repo_name |> Dream_html.respond diff --git a/lib/views.ml b/lib/views.ml index 446d601..2d11674 100644 --- a/lib/views.ml +++ b/lib/views.ml @@ -32,7 +32,7 @@ module Layout = struct ]; body [] [ - header "Ogit" body_data.subtitle; + header body_data.title body_data.subtitle; body_data.topnav; div [ id "main" ] body_data.content; footer; @@ -40,6 +40,60 @@ module Layout = struct ] end +module Topnav = struct + open Dream_html + open HTML + + let repo 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 + let url = + if String.length path = 0 then Printf.sprintf "/%s" repo_path + else Printf.sprintf "/%s/%s" repo_path path + in + li attrs [ a [ href "%s" url ] [ txt text ] ] + in + nav + [ id "top" ] + [ + ul [] + @@ List.map li_of_a + [ + ("", "summary"); + ("refs", "refs"); + ("log", "log"); + ("tree", "tree"); + ("commit", "commit"); + ("diff", "diff"); + ]; + ] + + (* let li_of_a (path, text) = *) + (* let is_active = path = current_path in *) + (* li (if is_active then [ id "active" ] else []) in *) + (* let link_of_path = *) + (* if String.length path = 0 then Printf.sprintf "/%s" repo_path *) + (* else Printf.sprintf "/%s/%s" repo_path path *) + (* in *) + (* li [ id "active" ] [ a [ href "%s" link_of_path ] [ txt text ] ] *) + (* in *) + (* nav *) + (* [ id "top" ] *) + (* [ *) + (* ul [] *) + (* @@ List.map li_of_a *) + (* [ *) + (* ("", "summary"); *) + (* ("refs", "refs"); *) + (* ("log", "log"); *) + (* ("tree", "tree"); *) + (* ("commit", "commit"); *) + (* ("diff", "diff"); *) + (* ]; *) + (* ] *) +end + module Ogit_root = struct open Dream_html open HTML @@ -56,7 +110,7 @@ module Ogit_root = struct let body_data = { - title = "My repositories"; + title = "Ogit"; subtitle = "Repositories for " ^ Config.author; topnav = null []; content = [ repositories_in Config.git_directory ]; @@ -68,57 +122,78 @@ end module Repo_root = struct open Dream_html open HTML - (* open Lwt.Syntax *) + + (* let href_for_git_object kind identifier = *) + (* let open Printf in *) + (* href "%s" @@ *) + (* match kind with *) + (* | `Branch -> sprintf "/tree/%s" identifier *) + (* | `Commit -> sprintf "/commit/%s" identifier *) let render repo_path = - (* let* title_result = Git_helpers.get_head_commit_hash repo_path in *) - (* let title = *) - (* match title_result with Ok hash -> hash | Error msg -> "Error: " ^ msg *) - (* in *) - let title = "Finble" in + let title = repo_path in let subtitle = Filename.concat Config.git_directory repo_path in - let topnav = - nav - [ id "top" ] - [ - ul [] - [ - li [] [ a [ href "/" ] [ txt "summary" ] ]; - li [] [ a [ href "/" ] [ txt "refs" ] ]; - li [] [ a [ href "/" ] [ txt "log" ] ]; - li [] [ a [ href "/" ] [ txt "tree" ] ]; - li [] [ a [ href "/" ] [ txt "commit" ] ]; - li [] [ a [ href "/" ] [ txt "diff" ] ]; - ]; - ] + let all_branches = Git_unhelpers.get_all_branches repo_path in + let li_of_branch branch = + li [] [ a [ href "%s" branch ] [ txt "%s" branch ] ] in - let recent_commits = Git_unhelpers.get_git_log repo_path 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 all_branches); h3 [] [ txt "Recent commits" ]; - ul [] @@ List.map li_of_commit recent_commits; + ul [] (List.map li_of_commit recent_commits); ] in - let body_data = { title; subtitle; topnav; content } in - Lwt.return @@ Layout.application body_data + 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 - open Lwt.Syntax + let full_path repo_path = Filename.concat Config.git_directory repo_path + + (* Helper function to list top-level files and directories *) + let ls_repo repo_path = + try + Sys.readdir (full_path repo_path) + |> Array.to_list + |> List.filter (fun name -> name <> ".git") (* Exclude .git *) + |> List.sort String.compare + with Sys_error _ -> [] + + (* Function to create a link based on file type *) + let link_for_entry repo_path entry = + let entry_path = Filename.concat (full_path repo_path) entry in + if Sys.is_directory entry_path then + a + [ href "%s" @@ Printf.sprintf "/%s/tree/%s" repo_path entry ] + [ txt "%s" (entry ^ "/") ] + else + a + [ href "%s" @@ Printf.sprintf "/%s/blob/%s" repo_path entry ] + [ txt "%s" entry ] + + let li_of_entry repo_path entry = li [] [ link_for_entry repo_path entry ] + + (* Render function *) let render repo_path = - let* title_result = Git_helpers.get_head_commit_hash repo_path in - let title = - match title_result with Ok hash -> hash | Error msg -> "Error: " ^ msg + let title = repo_path in + let subtitle = "Files" in + + let repo_entries = ls_repo repo_path in + let content = [ ul [] (List.map (li_of_entry repo_path) repo_entries) ] in + + let body_data = + { title; subtitle; topnav = Topnav.repo repo_path "tree"; content } in - let subtitle = "Dinglefops" in - let topnav = null [] in - let content = [ txt "foobar" ] in - let body_data = { title; subtitle; topnav; content } in - Lwt.return @@ Layout.application body_data + Layout.application body_data end |