Working basic htmx page and endpoint, WIP static file serving and dune test running
This commit is contained in:
		
						commit
						d18ae43731
					
				
							
								
								
									
										19
									
								
								.direnv/bin/nix-direnv-reload
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										19
									
								
								.direnv/bin/nix-direnv-reload
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
set -e
 | 
			
		||||
if [[ ! -d "/home/nate/Games/.source/stack_picker" ]]; then
 | 
			
		||||
  echo "Cannot find source directory; Did you move it?"
 | 
			
		||||
  echo "(Looking for "/home/nate/Games/.source/stack_picker")"
 | 
			
		||||
  echo 'Cannot force reload with this script - use "direnv reload" manually and then try again'
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# rebuild the cache forcefully
 | 
			
		||||
_nix_direnv_force_reload=1 direnv exec "/home/nate/Games/.source/stack_picker" true
 | 
			
		||||
 | 
			
		||||
# Update the mtime for .envrc.
 | 
			
		||||
# This will cause direnv to reload again - but without re-building.
 | 
			
		||||
touch "/home/nate/Games/.source/stack_picker/.envrc"
 | 
			
		||||
 | 
			
		||||
# Also update the timestamp of whatever profile_rc we have.
 | 
			
		||||
# This makes sure that we know we are up to date.
 | 
			
		||||
touch -r "/home/nate/Games/.source/stack_picker/.envrc" "/home/nate/Games/.source/stack_picker/.direnv"/*.rc
 | 
			
		||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
.direnv/**
 | 
			
		||||
_build/**
 | 
			
		||||
							
								
								
									
										2
									
								
								.ocamlformat
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.ocamlformat
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
version=0.26.2
 | 
			
		||||
profile=default
 | 
			
		||||
							
								
								
									
										26
									
								
								bin/dune
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								bin/dune
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
(executable
 | 
			
		||||
 (public_name stack_picker)
 | 
			
		||||
 (name main)
 | 
			
		||||
 (libraries
 | 
			
		||||
   stack_picker
 | 
			
		||||
   eio_main
 | 
			
		||||
   caqti-eio.unix
 | 
			
		||||
   cohttp-eio
 | 
			
		||||
   tyxml
 | 
			
		||||
   fmt
 | 
			
		||||
   unix
 | 
			
		||||
   logs.fmt
 | 
			
		||||
   logs.threaded)
 | 
			
		||||
 (preprocess
 | 
			
		||||
   (pps tyxml-ppx)))
 | 
			
		||||
(rule
 | 
			
		||||
 (alias deploy)
 | 
			
		||||
 (deps static)
 | 
			
		||||
 (action
 | 
			
		||||
  (chdir %{workspace_root}
 | 
			
		||||
    (run ./scripts/validate_html.ml ))))
 | 
			
		||||
(rule
 | 
			
		||||
 (alias static)
 | 
			
		||||
 (action
 | 
			
		||||
  (chdir %{workspace_root}
 | 
			
		||||
    (copy  static/* ./ ))))
 | 
			
		||||
							
								
								
									
										51
									
								
								bin/main.ml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								bin/main.ml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
open Tyxml
 | 
			
		||||
open Stack_picker.Srv
 | 
			
		||||
 | 
			
		||||
(* let () = Eio_main.run @@ fun env -> *)
 | 
			
		||||
(* let url = "sqlite3://stack.db" |> Uri.of_string in *)
 | 
			
		||||
(* Caqti_eio_unix.with_connection ~stdenv:(env :> Caqti_eio.stdenv) url (Stack_picker.Db.test env#stdout) *)
 | 
			
		||||
(* |> Caqti_eio.or_fail *)
 | 
			
		||||
let () = Logs.set_reporter (Logs_fmt.reporter ())
 | 
			
		||||
and () = Logs.Src.set_level Cohttp_eio.src (Some Debug)
 | 
			
		||||
 | 
			
		||||
let () =
 | 
			
		||||
  let port = ref 8080 in
 | 
			
		||||
  Arg.parse
 | 
			
		||||
    [ ("-p", Arg.Set_int port, " Listening port number(8080 by default)") ]
 | 
			
		||||
    ignore "An HTTP/1.1 server";
 | 
			
		||||
  Eio_main.run @@ fun env ->
 | 
			
		||||
  Eio.Switch.run @@ fun sw ->
 | 
			
		||||
  run ~env ~sw ~port:!port
 | 
			
		||||
    [
 | 
			
		||||
      get "/ping" (fun _req ->
 | 
			
		||||
          let num = 1 in
 | 
			
		||||
          let resp =
 | 
			
		||||
            Printf.sprintf "Hello this is the num: %d" num
 | 
			
		||||
            |> Eio.Flow.string_source
 | 
			
		||||
          in
 | 
			
		||||
          Stack_picker.Srv.respond ~body:resp ());
 | 
			
		||||
      get "/" (fun _ ->
 | 
			
		||||
          let%html html_doc =
 | 
			
		||||
            {|
 | 
			
		||||
            <html>
 | 
			
		||||
              <script src="https://unpkg.com/htmx.org@2.0.3"></script>
 | 
			
		||||
              <title>Ocaml</title>
 | 
			
		||||
              <body>
 | 
			
		||||
                <p>Hello from ocaml!</p>
 | 
			
		||||
                <button _hx-get="/shop" _hx-swap="outerHTML">Shop</button>
 | 
			
		||||
              </body>
 | 
			
		||||
            </html>|}
 | 
			
		||||
          in
 | 
			
		||||
          Stack_picker.Srv.respondHtmlDoc ~body:html_doc ());
 | 
			
		||||
      get "/shop" (fun _ ->
 | 
			
		||||
          let%html doc =
 | 
			
		||||
            {|
 | 
			
		||||
            <ol>
 | 
			
		||||
              <li>Bat</li>
 | 
			
		||||
              <li>Cat</li>
 | 
			
		||||
              <li>Doggie</li>
 | 
			
		||||
            </ol>
 | 
			
		||||
            |}
 | 
			
		||||
          in
 | 
			
		||||
          respondHtml ~body:doc ());
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										26
									
								
								dune-project
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								dune-project
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
(lang dune 3.15)
 | 
			
		||||
 | 
			
		||||
(name stack_picker)
 | 
			
		||||
 | 
			
		||||
(generate_opam_files true)
 | 
			
		||||
 | 
			
		||||
(source
 | 
			
		||||
 (github username/reponame))
 | 
			
		||||
 | 
			
		||||
(authors "Author Name")
 | 
			
		||||
 | 
			
		||||
(maintainers "Maintainer Name")
 | 
			
		||||
 | 
			
		||||
(license LICENSE)
 | 
			
		||||
 | 
			
		||||
(documentation https://url/to/documentation)
 | 
			
		||||
 | 
			
		||||
(package
 | 
			
		||||
 (name stack_picker)
 | 
			
		||||
 (synopsis "A short synopsis")
 | 
			
		||||
 (description "A longer description")
 | 
			
		||||
 (depends ocaml dune)
 | 
			
		||||
 (tags
 | 
			
		||||
  (topics "to describe" your project)))
 | 
			
		||||
 | 
			
		||||
; See the complete stanza docs at https://dune.readthedocs.io/en/stable/dune-files.html#dune-project
 | 
			
		||||
							
								
								
									
										116
									
								
								flake.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								flake.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							@ -0,0 +1,116 @@
 | 
			
		||||
{
 | 
			
		||||
  "nodes": {
 | 
			
		||||
    "flake-utils": {
 | 
			
		||||
      "inputs": {
 | 
			
		||||
        "systems": "systems"
 | 
			
		||||
      },
 | 
			
		||||
      "locked": {
 | 
			
		||||
        "lastModified": 1726560853,
 | 
			
		||||
        "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
 | 
			
		||||
        "owner": "numtide",
 | 
			
		||||
        "repo": "flake-utils",
 | 
			
		||||
        "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      },
 | 
			
		||||
      "original": {
 | 
			
		||||
        "owner": "numtide",
 | 
			
		||||
        "repo": "flake-utils",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "flake-utils_2": {
 | 
			
		||||
      "inputs": {
 | 
			
		||||
        "systems": "systems_2"
 | 
			
		||||
      },
 | 
			
		||||
      "locked": {
 | 
			
		||||
        "lastModified": 1726560853,
 | 
			
		||||
        "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
 | 
			
		||||
        "owner": "numtide",
 | 
			
		||||
        "repo": "flake-utils",
 | 
			
		||||
        "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      },
 | 
			
		||||
      "original": {
 | 
			
		||||
        "owner": "numtide",
 | 
			
		||||
        "repo": "flake-utils",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "nixpkgs": {
 | 
			
		||||
      "locked": {
 | 
			
		||||
        "lastModified": 1727634051,
 | 
			
		||||
        "narHash": "sha256-S5kVU7U82LfpEukbn/ihcyNt2+EvG7Z5unsKW9H/yFA=",
 | 
			
		||||
        "owner": "nixos",
 | 
			
		||||
        "repo": "nixpkgs",
 | 
			
		||||
        "rev": "06cf0e1da4208d3766d898b7fdab6513366d45b9",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      },
 | 
			
		||||
      "original": {
 | 
			
		||||
        "owner": "nixos",
 | 
			
		||||
        "ref": "nixos-unstable",
 | 
			
		||||
        "repo": "nixpkgs",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "ocaml-overlay": {
 | 
			
		||||
      "inputs": {
 | 
			
		||||
        "flake-utils": "flake-utils_2",
 | 
			
		||||
        "nixpkgs": [
 | 
			
		||||
          "nixpkgs"
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "locked": {
 | 
			
		||||
        "lastModified": 1729147101,
 | 
			
		||||
        "narHash": "sha256-Th67Tza+DSGeKPTHy451EBJ+WZzaCKnAYHacdVLxO4s=",
 | 
			
		||||
        "owner": "nix-ocaml",
 | 
			
		||||
        "repo": "nix-overlays",
 | 
			
		||||
        "rev": "f4430b8e902af054b432c546d9423a254b9bf46f",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      },
 | 
			
		||||
      "original": {
 | 
			
		||||
        "owner": "nix-ocaml",
 | 
			
		||||
        "repo": "nix-overlays",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "root": {
 | 
			
		||||
      "inputs": {
 | 
			
		||||
        "flake-utils": "flake-utils",
 | 
			
		||||
        "nixpkgs": "nixpkgs",
 | 
			
		||||
        "ocaml-overlay": "ocaml-overlay"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "systems": {
 | 
			
		||||
      "locked": {
 | 
			
		||||
        "lastModified": 1681028828,
 | 
			
		||||
        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
 | 
			
		||||
        "owner": "nix-systems",
 | 
			
		||||
        "repo": "default",
 | 
			
		||||
        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      },
 | 
			
		||||
      "original": {
 | 
			
		||||
        "owner": "nix-systems",
 | 
			
		||||
        "repo": "default",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "systems_2": {
 | 
			
		||||
      "locked": {
 | 
			
		||||
        "lastModified": 1681028828,
 | 
			
		||||
        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
 | 
			
		||||
        "owner": "nix-systems",
 | 
			
		||||
        "repo": "default",
 | 
			
		||||
        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      },
 | 
			
		||||
      "original": {
 | 
			
		||||
        "owner": "nix-systems",
 | 
			
		||||
        "repo": "default",
 | 
			
		||||
        "type": "github"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "root": "root",
 | 
			
		||||
  "version": 7
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								flake.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								flake.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
{
 | 
			
		||||
  description = "A very basic flake";
 | 
			
		||||
 | 
			
		||||
  inputs = {
 | 
			
		||||
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
 | 
			
		||||
    flake-utils.url = "github:numtide/flake-utils";
 | 
			
		||||
    # Add support for ocaml package overlay
 | 
			
		||||
    ocaml-overlay.url = "github:nix-ocaml/nix-overlays";
 | 
			
		||||
    ocaml-overlay.inputs.nixpkgs.follows = "nixpkgs";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  outputs = { self, flake-utils, nixpkgs, ocaml-overlay }:
 | 
			
		||||
    flake-utils.lib.eachDefaultSystem (system: let
 | 
			
		||||
      pkgs = import nixpkgs {
 | 
			
		||||
        inherit system;
 | 
			
		||||
        overlays = [
 | 
			
		||||
          ocaml-overlay.overlays.default
 | 
			
		||||
        ];
 | 
			
		||||
      };
 | 
			
		||||
      # set ocaml packages' version
 | 
			
		||||
      ocamlPackages = pkgs.ocaml-ng.ocamlPackages_5_2;
 | 
			
		||||
    in {
 | 
			
		||||
      devShell = 
 | 
			
		||||
      #let
 | 
			
		||||
        #
 | 
			
		||||
      #in
 | 
			
		||||
        pkgs.mkShell {
 | 
			
		||||
          buildInputs = with pkgs; [
 | 
			
		||||
            ocaml
 | 
			
		||||
            ocamlPackages.ocaml-lsp
 | 
			
		||||
            ocamlPackages.findlib
 | 
			
		||||
            ocamlPackages.ocamlformat
 | 
			
		||||
            ocamlPackages.ocamlformat-rpc-lib
 | 
			
		||||
            ocamlPackages.utop
 | 
			
		||||
            dune_3
 | 
			
		||||
            # Project deps
 | 
			
		||||
            ocamlPackages.eio_main
 | 
			
		||||
            ocamlPackages.caqti-eio
 | 
			
		||||
            ocamlPackages.caqti-driver-sqlite3
 | 
			
		||||
            ocamlPackages.ocaml_sqlite3
 | 
			
		||||
            ocamlPackages.dream
 | 
			
		||||
            ocamlPackages.cohttp-eio
 | 
			
		||||
            ocamlPackages.tyxml
 | 
			
		||||
            ocamlPackages.tyxml-ppx
 | 
			
		||||
            ocamlPackages.ppx_deriving
 | 
			
		||||
            ocamlPackages.ppx_deriving_yojson
 | 
			
		||||
            ocamlPackages.ppx_assert
 | 
			
		||||
            ocamlPackages.ppx_inline_test
 | 
			
		||||
          ];
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								lib/db.ml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/db.ml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
open Base
 | 
			
		||||
 | 
			
		||||
let%test_unit "rev" =
 | 
			
		||||
  [%test_eq: int list] (List.rev [ 3; 2; 1 ]) [ 1; 2; 3 ]
 | 
			
		||||
 | 
			
		||||
let test stdout db = 
 | 
			
		||||
  let _ = stdout in
 | 
			
		||||
  let _ = db in
 | 
			
		||||
  Ok ()
 | 
			
		||||
							
								
								
									
										15
									
								
								lib/dune
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/dune
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
(library
 | 
			
		||||
 (name stack_picker)
 | 
			
		||||
 (inline_tests)
 | 
			
		||||
 (libraries
 | 
			
		||||
   eio_main
 | 
			
		||||
   caqti-eio.unix
 | 
			
		||||
   caqti-driver-sqlite3
 | 
			
		||||
   cohttp-eio
 | 
			
		||||
   tyxml
 | 
			
		||||
   fmt
 | 
			
		||||
   unix
 | 
			
		||||
   logs.fmt
 | 
			
		||||
   logs.threaded)
 | 
			
		||||
 (preprocess
 | 
			
		||||
   (pps ppx_assert ppx_inline_test tyxml-ppx)))
 | 
			
		||||
							
								
								
									
										145
									
								
								lib/srv.ml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								lib/srv.ml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,145 @@
 | 
			
		||||
open Tyxml
 | 
			
		||||
 | 
			
		||||
type endpoint_key = {
 | 
			
		||||
  path : string;
 | 
			
		||||
  callback : Http.Request.t -> Http.Response.t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type opt_route_response = {
 | 
			
		||||
  headers : Http.Header.t option;
 | 
			
		||||
  status : Http.Status.t option;
 | 
			
		||||
  body : Cohttp_eio.Body.t option;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type route_response = {
 | 
			
		||||
  headers : Http.Header.t;
 | 
			
		||||
  status : Http.Status.t;
 | 
			
		||||
  body : Cohttp_eio.Body.t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type route_callback = Http.Request.t -> route_response
 | 
			
		||||
 | 
			
		||||
module Endpath : sig
 | 
			
		||||
  type t
 | 
			
		||||
 | 
			
		||||
  val compare : t -> t -> int
 | 
			
		||||
  val of_tuple : Http.Method.t * string -> t
 | 
			
		||||
end = struct
 | 
			
		||||
  type t = Http.Method.t * string
 | 
			
		||||
 | 
			
		||||
  let compare (meth_a, path_a) (meth_b, path_b) =
 | 
			
		||||
    let meth_compare = Http.Method.compare meth_a meth_b in
 | 
			
		||||
    if meth_compare <> 0 then meth_compare
 | 
			
		||||
    else String.(compare (lowercase_ascii path_a) (lowercase_ascii path_b))
 | 
			
		||||
 | 
			
		||||
  let of_tuple (method_, path) : t = (method_, path)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
module EndpointMap = Map.Make (Endpath)
 | 
			
		||||
 | 
			
		||||
(* let endpoints: route_callback EndpointMap.t ref = ref EndpointMap.empty *)
 | 
			
		||||
 | 
			
		||||
let default_headers =
 | 
			
		||||
  let headers_strings = [ ("Content-Type", "text/html; charset=UTF-8") ] in
 | 
			
		||||
  ref (Http.Header.of_list headers_strings)
 | 
			
		||||
 | 
			
		||||
let default_body = ref ("" |> Eio.Flow.string_source)
 | 
			
		||||
let log_warning ex = Logs.warn (fun f -> f "%a" Eio.Exn.pp ex)
 | 
			
		||||
 | 
			
		||||
let get (path : string) (callback : route_callback) =
 | 
			
		||||
  let key = Endpath.of_tuple (`GET, path) in
 | 
			
		||||
  (key, callback)
 | 
			
		||||
 | 
			
		||||
let post (path : string) (callback : route_callback) =
 | 
			
		||||
  let key = Endpath.of_tuple (`POST, path) in
 | 
			
		||||
  (key, callback)
 | 
			
		||||
(* endpoints :=  !endpoints |> EndpointMap.add key callback *)
 | 
			
		||||
 | 
			
		||||
let rec find_html_files (static_path : string) =
 | 
			
		||||
  let files = Sys.readdir static_path in
 | 
			
		||||
  Array.fold_left
 | 
			
		||||
    (fun acc file ->
 | 
			
		||||
      let path = Filename.concat static_path file in
 | 
			
		||||
      if Sys.is_directory path then acc @ find_html_files path
 | 
			
		||||
      else if Filename.check_suffix path ".html" then path :: acc
 | 
			
		||||
      else acc)
 | 
			
		||||
    [] files
 | 
			
		||||
 | 
			
		||||
let respond ?body ?headers ?status () =
 | 
			
		||||
  {
 | 
			
		||||
    body = Option.value body ~default:!default_body;
 | 
			
		||||
    headers = Option.value headers ~default:!default_headers;
 | 
			
		||||
    status = Option.value status ~default:`OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
let respondStr ?body ?headers ?status () =
 | 
			
		||||
  let eio_body =
 | 
			
		||||
    match body with Some b -> Eio.Flow.string_source b | None -> !default_body
 | 
			
		||||
  in
 | 
			
		||||
  {
 | 
			
		||||
    body = eio_body;
 | 
			
		||||
    headers = Option.value headers ~default:!default_headers;
 | 
			
		||||
    status = Option.value status ~default:`OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
let respondHtmlDoc ?body ?headers ?status () =
 | 
			
		||||
  let eio_body =
 | 
			
		||||
    match body with
 | 
			
		||||
    | Some b ->
 | 
			
		||||
        let content = Format.asprintf "%a" (Html.pp ()) b in
 | 
			
		||||
        Eio.Flow.string_source content
 | 
			
		||||
    | None -> !default_body
 | 
			
		||||
  in
 | 
			
		||||
  {
 | 
			
		||||
    body = eio_body;
 | 
			
		||||
    headers = Option.value headers ~default:!default_headers;
 | 
			
		||||
    status = Option.value status ~default:`OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
let respondHtml ?body ?headers ?status () =
 | 
			
		||||
  let eio_body =
 | 
			
		||||
    match body with
 | 
			
		||||
    | Some b ->
 | 
			
		||||
        let content = Format.asprintf "%a" (Html.pp_elt ()) b in
 | 
			
		||||
        Eio.Flow.string_source content
 | 
			
		||||
    | None -> !default_body
 | 
			
		||||
  in
 | 
			
		||||
  {
 | 
			
		||||
    body = eio_body;
 | 
			
		||||
    headers = Option.value headers ~default:!default_headers;
 | 
			
		||||
    status = Option.value status ~default:`OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
(** TODO not implemented, needs yojson ? *)
 | 
			
		||||
let respondJson body ?headers ?status () =
 | 
			
		||||
  {
 | 
			
		||||
    body;
 | 
			
		||||
    headers = Option.value headers ~default:!default_headers;
 | 
			
		||||
    status = Option.value status ~default:`OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
let run ~env ~sw ~port endpoint_list =
 | 
			
		||||
  let endpoints : route_callback EndpointMap.t =
 | 
			
		||||
    EndpointMap.of_list endpoint_list
 | 
			
		||||
  in
 | 
			
		||||
  let socket =
 | 
			
		||||
    Eio.Net.listen env#net ~sw ~backlog:128 ~reuse_addr:true
 | 
			
		||||
      (`Tcp (Eio.Net.Ipaddr.V4.loopback, port))
 | 
			
		||||
  in
 | 
			
		||||
  let handler _socket request _body =
 | 
			
		||||
    (* let req = Http.Request.resource request in *)
 | 
			
		||||
    let path = Http.Request.resource request in
 | 
			
		||||
    let end_key = Endpath.of_tuple (request.meth, path) in
 | 
			
		||||
    let opt_path = EndpointMap.find_opt end_key endpoints in
 | 
			
		||||
    match opt_path with
 | 
			
		||||
    | Some route ->
 | 
			
		||||
        (* NOTE needs an Exn.protect here? try / finally *)
 | 
			
		||||
        let response = route request in
 | 
			
		||||
        Cohttp_eio.Server.respond () ~headers:response.headers
 | 
			
		||||
          ~status:response.status ~body:response.body
 | 
			
		||||
    | None ->
 | 
			
		||||
        let not_found_body = "Route not defined\n" |> Eio.Flow.string_source in
 | 
			
		||||
        Cohttp_eio.Server.respond () ~status:`Not_found ~body:not_found_body
 | 
			
		||||
  in
 | 
			
		||||
  let server = Cohttp_eio.Server.make ~callback:handler () in
 | 
			
		||||
  Cohttp_eio.Server.run socket server ~on_error:log_warning
 | 
			
		||||
							
								
								
									
										15
									
								
								scripts/dune
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								scripts/dune
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
(library
 | 
			
		||||
 (name stack_picker_scripts)
 | 
			
		||||
 (inline_tests)
 | 
			
		||||
 (libraries
 | 
			
		||||
   eio_main
 | 
			
		||||
   caqti-eio.unix
 | 
			
		||||
   caqti-driver-sqlite3
 | 
			
		||||
   cohttp-eio
 | 
			
		||||
   tyxml
 | 
			
		||||
   fmt
 | 
			
		||||
   unix
 | 
			
		||||
   logs.fmt
 | 
			
		||||
   logs.threaded)
 | 
			
		||||
 (preprocess
 | 
			
		||||
   (pps ppx_assert ppx_inline_test tyxml-ppx)))
 | 
			
		||||
							
								
								
									
										19
									
								
								scripts/validate_html.ml
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										19
									
								
								scripts/validate_html.ml
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
#!/usr/bin/env ocaml
 | 
			
		||||
 | 
			
		||||
let rec find_html_files (static_path : string) =
 | 
			
		||||
  let files = Sys.readdir static_path in
 | 
			
		||||
  Array.fold_left
 | 
			
		||||
    (fun acc file ->
 | 
			
		||||
      let path = Filename.concat static_path file in
 | 
			
		||||
      if Sys.is_directory path then acc @ find_html_files path
 | 
			
		||||
      else if Filename.check_suffix path ".html" then path :: acc
 | 
			
		||||
      else acc)
 | 
			
		||||
    [] files
 | 
			
		||||
 | 
			
		||||
let () =
 | 
			
		||||
  let files = find_html_files "static" in
 | 
			
		||||
  let _file_names = List.fold_left (fun name acc ->
 | 
			
		||||
    acc ^ "\n - " ^ name
 | 
			
		||||
  ) "" files in
 | 
			
		||||
  (* Printf.printf "Found files:\n%s" file_names; *)
 | 
			
		||||
  exit 0
 | 
			
		||||
							
								
								
									
										31
									
								
								stack_picker.opam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								stack_picker.opam
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
# This file is generated by dune, edit dune-project instead
 | 
			
		||||
opam-version: "2.0"
 | 
			
		||||
synopsis: "A short synopsis"
 | 
			
		||||
description: "A longer description"
 | 
			
		||||
maintainer: ["Maintainer Name"]
 | 
			
		||||
authors: ["Author Name"]
 | 
			
		||||
license: "LICENSE"
 | 
			
		||||
tags: ["topics" "to describe" "your" "project"]
 | 
			
		||||
homepage: "https://github.com/username/reponame"
 | 
			
		||||
doc: "https://url/to/documentation"
 | 
			
		||||
bug-reports: "https://github.com/username/reponame/issues"
 | 
			
		||||
depends: [
 | 
			
		||||
  "ocaml"
 | 
			
		||||
  "dune" {>= "3.15"}
 | 
			
		||||
  "odoc" {with-doc}
 | 
			
		||||
]
 | 
			
		||||
build: [
 | 
			
		||||
  ["dune" "subst"] {dev}
 | 
			
		||||
  [
 | 
			
		||||
    "dune"
 | 
			
		||||
    "build"
 | 
			
		||||
    "-p"
 | 
			
		||||
    name
 | 
			
		||||
    "-j"
 | 
			
		||||
    jobs
 | 
			
		||||
    "@install"
 | 
			
		||||
    "@runtest" {with-test}
 | 
			
		||||
    "@doc" {with-doc}
 | 
			
		||||
  ]
 | 
			
		||||
]
 | 
			
		||||
dev-repo: "git+https://github.com/username/reponame.git"
 | 
			
		||||
							
								
								
									
										0
									
								
								static/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								static/index.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								static/temp/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								static/temp/index.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								static/temp/shop/store.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								static/temp/shop/store.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								static/temp/shop/test.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								static/temp/shop/test.txt
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								test/dune
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								test/dune
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
(test
 | 
			
		||||
 (name test_stack_picker)
 | 
			
		||||
 (libraries eio_main sqlite3 dream))
 | 
			
		||||
							
								
								
									
										1
									
								
								test/test_stack_picker.ml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/test_stack_picker.ml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user