diff options
| author | Marius Peter <marius.peter@tutanota.com> | 2025-05-18 17:02:32 +0200 | 
|---|---|---|
| committer | Marius Peter <marius.peter@tutanota.com> | 2025-05-18 17:02:32 +0200 | 
| commit | 6658535cf610d6c1d99dae20e98ced14a920dbb2 (patch) | |
| tree | f67c4076e4e2d8c9778995fa5880490552133f29 /lib | |
| parent | 3073ec0e7e2e7f0a89dbc4d4851da2451aff2f62 (diff) | |
Modularize everything.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/git_helpers.ml | 91 | ||||
| -rw-r--r-- | lib/handlers.ml | 54 | ||||
| -rw-r--r-- | lib/static/styles.css | 7 | ||||
| -rw-r--r-- | lib/views.ml | 215 | 
4 files changed, 204 insertions, 163 deletions
| diff --git a/lib/git_helpers.ml b/lib/git_helpers.ml index 5e5d4cd..ed917a8 100644 --- a/lib/git_helpers.ml +++ b/lib/git_helpers.ml @@ -4,13 +4,6 @@ module Value = Git.Value  type user_record = { name : string; email : string } -type commit_record = { -  hash : string; -  parents : string list; -  author : Git.User.t; -  message : string option; -} -  let full_path path = Filename.concat Config.git_directory path  let store repo_path = @@ -23,39 +16,51 @@ let repo_description repo_path =    let description_path = Filename.concat (full_path repo_path) "description" in    In_channel.with_open_text description_path In_channel.input_all -(* Read a Git object and turn it into our [commit_record], or propagate an error. *) -let get_commit_record store h = -  Store.read store h >>= function -  | Ok (Value.Commit c) -> -      Lwt.return_ok -        { -          hash = Store.Hash.to_hex h; -          parents = Store.Value.Commit.parents c |> List.map Store.Hash.to_hex; -          author = Store.Value.Commit.author c; -          message = Store.Value.Commit.message c; -        } -  | Ok _ -> Lwt.return_error (`Msg "object is not a commit") -  | Error e -> Lwt.return_error e - -let recent_commits repo_path n = -  let open Lwt_result.Syntax in -  let* store = store repo_path in -  let* head = Store.Ref.resolve store Git.Reference.head in -  let rec walk acc hash count = -    if count = 0 then Lwt.return_ok (List.rev acc) -    else -      get_commit_record store hash >>= function -      | Error e -> Lwt.return_error e -      | Ok commit -> ( -          match commit.parents with -          | parent :: _ -> -              walk (commit :: acc) (Store.Hash.of_hex parent) (count - 1) -          | [] -> Lwt.return_ok (List.rev (commit :: acc))) -  in -  walk [] head n - -let get_commit repo_path id = -  let open Lwt_result.Syntax in -  let* store = store repo_path in -  let id = Store.Hash.of_hex id in -  get_commit_record store id +module Commit = struct +  type t = { +    hash : string; +    parents : string list; +    author : Git.User.t; +    message : string option; +  } + +  let of_hash store h = +    Store.read store h >>= function +    | Ok (Value.Commit c) -> +        Lwt_result.return +          { +            hash = Store.Hash.to_hex h; +            parents = Store.Value.Commit.parents c |> List.map Store.Hash.to_hex; +            author = Store.Value.Commit.author c; +            message = Store.Value.Commit.message c; +          } +    | Ok _ -> Lwt.return_error (`Msg "object is not a commit") +    | Error e -> Lwt.return_error e + +  let recent_commits repo_path n = +    let open Lwt_result.Syntax in +    let* store = store repo_path in +    let* head = Store.Ref.resolve store Git.Reference.head in +    let rec walk acc hash count = +      if count = 0 then Lwt_result.return (List.rev acc) +      else +        of_hash store hash >>= function +        | Error e -> Lwt.return_error e +        | Ok commit -> ( +            match commit.parents with +            | parent :: _ -> +                walk (commit :: acc) (Store.Hash.of_hex parent) (count - 1) +            | [] -> Lwt_result.return (List.rev (commit :: acc))) +    in +    walk [] head n + +  let of_id repo_path id = +    let open Lwt_result.Syntax in +    let* store = store repo_path in +    let id = Store.Hash.of_hex id in +    of_hash store id +end + +module Branch = struct +  let all_branches repo_path = [ "foo"; "bar"; repo_path ] +end diff --git a/lib/handlers.ml b/lib/handlers.ml index 95aecd3..321c693 100644 --- a/lib/handlers.ml +++ b/lib/handlers.ml @@ -6,41 +6,39 @@ let ( let* ) m f =        let msg = Format.asprintf "%a" Git_unix.Store.pp_error e in        Views.error_page msg |> Dream_html.respond -let ogit_root _req = Views.ogit_root () |> Dream_html.respond +let root _req = Views.root () |> Dream_html.respond -let repo_summary req = -  let repo_path = Dream.param req "repo_name" in -  let branches = [ "bing"; "bong" ] in -  let* commits = Git_helpers.recent_commits repo_path 10 in -  Views.repo_summary repo_path branches commits |> Dream_html.respond +module Repo = struct +  let repo_path req = Dream.param req "repo_name" -let repo_commit req = -  let repo_path = Dream.param req "repo_name" in -  let id = match Dream.query req "id" with Some id -> id | None -> "" in -  let* commit = Git_helpers.get_commit repo_path id in -  Views.repo_commit repo_path commit |> Dream_html.respond +  let summary req = +    let branches = Git_helpers.Branch.all_branches (repo_path req) in +    let* commits = Git_helpers.Commit.recent_commits (repo_path req) 10 in +    let authors = [ "John Pork"; "Sebastian Jellybean" ] in +    Views.Repo.summary (repo_path req) branches commits authors +    |> 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 refs req = Views.Repo.refs (repo_path req) |> Dream_html.respond +  let log req = Views.Repo.log (repo_path req) |> Dream_html.respond +  let tree req = Views.Repo.tree (repo_path req) |> 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 commit req = +    let id = match Dream.query req "id" with Some id -> id | None -> "" in +    let* commit = Git_helpers.Commit.of_id (repo_path req) id in +    Views.Repo.commit (repo_path req) commit |> Dream_html.respond +end  let all_handlers =    [ -    Dream.get "/" ogit_root; +    Dream.get "/" root;      Dream.scope "/:repo_name" [] -      [ -        Dream.get "/" repo_summary; -        Dream.get "/commit/" repo_commit; -        (* Dream.get "/tree" repo_tree; *) -        (* Dream.get "/blob" repo_blob; *) -      ]; +      Repo. +        [ +          Dream.get "/" summary; +          Dream.get "/refs/" refs; +          Dream.get "/log/" log; +          Dream.get "/tree/" tree; +          Dream.get "/commit/" commit; +        ];      Dream.get "/static/**" (Dream.static "./lib/static");    ] diff --git a/lib/static/styles.css b/lib/static/styles.css index de2c3db..ae8a86c 100644 --- a/lib/static/styles.css +++ b/lib/static/styles.css @@ -69,3 +69,10 @@ div#main a:hover {      color: black;      text-decoration: revert;  } + +h1 { +    position: sticky; +    top: 0; +    background: inherit; +    padding: 0.5em 0; +} diff --git a/lib/views.ml b/lib/views.ml index 3c1330e..e023236 100644 --- a/lib/views.ml +++ b/lib/views.ml @@ -3,11 +3,39 @@ type head_data = { page_title : string }  type body_data = {    title : string;    subtitle : string; -  topnav : Dream_html.node;    content : Dream_html.node list;  } -module Layout = struct +module Components = struct +  open Dream_html + +  let topnav current_path = +    let open HTML in +    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.equal path "/" then Printf.sprintf "/%s/" current_path +        else Printf.sprintf "/%s/%s" current_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"); +             ]; +      ] +end + +module Page = struct    open Dream_html    open HTML @@ -22,7 +50,7 @@ module Layout = struct    let default_head_data = { page_title = "Ogit" } -  let application ?(head_data = default_head_data) body_data = +  let render ?(head_data = default_head_data) body_data =      html []        [          head [] @@ -35,44 +63,14 @@ module Layout = struct          body []            [              header body_data.title body_data.subtitle; -            body_data.topnav; +            Components.topnav body_data.title;              div [ id "main" ] body_data.content;              footer ();            ];        ]  end -module Components = struct -  open Dream_html - -  let topnav repo_path current_path = -    let open HTML in -    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.equal path "/" 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"); -             ]; -      ] -end - -let ogit_root () = +let root () =    let open Dream_html in    let repositories_in directory =      let repos = @@ -87,60 +85,88 @@ let ogit_root () =      {        title = "Ogit";        subtitle = "Repositories for " ^ Config.author; -      topnav = HTML.(null []);        content = [ repositories_in Config.git_directory ];      }    in -  Layout.application body_data +  Page.render body_data -let repo_summary repo_path branches commits = -  let open Git_helpers in -  let open Dream_html in -  let li_of_branch branch = -    HTML.(li [] [ a [ href "%s" branch ] [ txt "%s" branch ] ]) -  in -  let li_of_commit commit = -    match commit.message with -    | Some msg -> -        HTML.( -          li [] -            [ -              a -                [ href "commit/?id=%s" commit.hash ] -                [ txt "%s %s" (short_hash commit.hash) msg ]; -            ]) -    | None -> HTML.(li [] [ a [ href "" ] [ txt "caca!!" ] ]) -  in -  let content = -    HTML. -      [ -        h3 [] [ txt "Branches" ]; -        ul [] (List.map li_of_branch branches); -        h3 [] [ txt "Recent commits" ]; -        ul [] (List.map li_of_commit commits); -      ] -  in -  Layout.application -    { -      title = repo_path; -      subtitle = repo_description repo_path; -      topnav = Components.topnav repo_path ""; -      content; -    } +module Repo = struct +  let summary repo_path branches commits authors = +    let open Dream_html in +    let li_of_branch branch = +      HTML.(li [] [ a [ href "%s" branch ] [ txt "%s" branch ] ]) +    in +    let li_of_commit (commit : Git_helpers.Commit.t) = +      match commit.message with +      | Some msg -> +          HTML.( +            li [] +              [ +                a +                  [ href "commit/?id=%s" commit.hash ] +                  [ txt "%s %s" (Git_helpers.short_hash commit.hash) msg ]; +              ]) +      | None -> HTML.(li [] [ a [ href "" ] [ txt "caca!!" ] ]) +    in +    let li_of_author author = +      HTML.(li [] [ a [ href "" ] [ txt "%s" author ] ]) +    in +    let content = +      HTML. +        [ +          h3 [] [ txt "Branches" ]; +          ul [] (List.map li_of_branch branches); +          h3 [] [ txt "Recent commits" ]; +          ul [] (List.map li_of_commit commits); +          h3 [] [ txt "Authors" ]; +          ul [] (List.map li_of_author authors); +        ] +    in +    Page.render +      { +        title = repo_path; +        subtitle = Git_helpers.repo_description repo_path; +        content; +      } -let repo_commit repo_path commit = -  let open Git_helpers in -  let open Dream_html in -  let message = match commit.message with Some msg -> msg | None -> "" in -  let content = HTML.[ h3 [] [ txt "%s" message ] ] in -  let title = Printf.sprintf "%s : %s" repo_path (short_hash commit.hash) in -  Layout.application -    { -      title; -      subtitle = ""; -      topnav = Components.topnav repo_path ""; -      content; -    } +  let refs repo_path = +    let open Dream_html in +    Page.render +      HTML. +        { +          title = repo_path; +          subtitle = Git_helpers.repo_description repo_path; +          content = [ null [] ]; +        } + +  let log repo_path = +    let open Dream_html in +    Page.render +      HTML. +        { +          title = repo_path; +          subtitle = Git_helpers.repo_description repo_path; +          content = [ null [] ]; +        } + +  let tree repo_path = +    let open Dream_html in +    Page.render +      HTML. +        { +          title = repo_path; +          subtitle = Git_helpers.repo_description repo_path; +          content = [ null [] ]; +        } + +  let commit repo_path (commit : Git_helpers.Commit.t) = +    let open Dream_html in +    let open Git_helpers in +    let message = match commit.message with Some msg -> msg | None -> "" in +    let content = HTML.[ h3 [] [ txt "%s" message ] ] in +    let title = Printf.sprintf "%s : %s" repo_path (short_hash commit.hash) in +    Page.render { title; subtitle = ""; content } +end  let error_page message =    let open Dream_html in @@ -149,19 +175,24 @@ let error_page message =        [          head []            [ -            title [] "Big error"; +            title [] "Fatal 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 ]; +            h1 [] [ txt "Fatal Error" ]; +            div +              [ id "main" ] +              [ +                p [] [ b [] [ txt "%s" message ] ]; +                p [] +                  [ +                    txt +                      "Your best course of action is to press the 'back' \ +                       button in your browser."; +                  ]; +              ];            ];        ]) - -let repo_tree repo_path = repo_summary repo_path -let repo_blob repo_path = repo_summary repo_path | 
