diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml deleted file mode 100644 index f166ba6..0000000 --- a/.github/workflows/format.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Format -on: - push: - branches: - - master -jobs: - build: - name: Format OCaml code - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - uses: avsm/setup-ocaml@v1.0 - with: - ocaml-version: 4.10.0 - - name: Install dependencies - run: | - opam pin add routes.dev -n . - opam depext -yt routes - opam install -t . --deps-only - - name: Build - run: opam exec -- dune build @install - - name: Format - run: opam exec -- dune build @fmt --auto-promote || true - - name: Create Pull Request - uses: peter-evans/create-pull-request@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: ocamlformat fixes - author-name: Anurag Soni - author-email: anurag@sonianurag.com - title: ocamlformat fixes - body: This is an auto-generated PR with fixes by ocamlformat. - labels: ocamlformat, automated pr - reviewers: anuragsoni diff --git a/.github/workflows/post_merge.yml b/.github/workflows/post_merge.yml index aa13233..447f2d3 100644 --- a/.github/workflows/post_merge.yml +++ b/.github/workflows/post_merge.yml @@ -10,8 +10,8 @@ jobs: strategy: fail-fast: false matrix: - operating-system: [ubuntu-latest] - ocaml-version: [ '4.10.0' , '4.09.1', '4.08.1', '4.07.1', '4.06.1' ] + operating-system: [windows-latest] + ocaml-version: [ '4.10.0' , '4.09.1', '4.08.1', '4.07.1', '4.06.1', '4.05.0' ] steps: - uses: actions/checkout@master - uses: avsm/setup-ocaml@v1.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a851703..53e53a9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - ocaml-version: [ '4.10.0' , '4.09.1', '4.08.1', '4.07.1', '4.06.1' ] + ocaml-version: [ '4.10.0' , '4.09.1', '4.08.1', '4.07.1', '4.06.1', '4.05.0' ] steps: - uses: actions/checkout@master - uses: avsm/setup-ocaml@v1.0 diff --git a/README.md b/README.md index 6f8fbfc..968a0e8 100644 --- a/README.md +++ b/README.md @@ -7,102 +7,85 @@ reasonable performance [See benchmark folder](https://github.com/anuragsoni/rout Users can create a list of routes, and handler function to work on the extracted entities using the combinators provided by the library. To perform URL matching one would just need to forward -the URL's path and query to the matcher. +the URL's path to the router. #### Example ```ocaml -# #require "routes";; -# type req = { target: string };; -type req = { target : string; } - -# let idx (_ : req) = "root";; -val idx : req -> string = - -# let get_user (id : int) (req : req) = Printf.sprintf "Received request from %s to fetch id: %d" req.target id;; -val get_user : int -> req -> string = - -# let search_user (name: string) (city: string) (_req : req) = "search for user";; -val search_user : string -> string -> req -> string = - -# let routes = Routes.( - one_of [ - nil @--> idx - ; (s "user" / int /? nil) @--> get_user - ; (s "user" / str / str /? trail) @--> search_user - ]);; -val routes : (req -> string) Routes.router = - -# let req = { target = "/user/12" };; -val req : req = {target = "/user/12"} - -# match Routes.match' ~target:"/some/url" routes with None -> "No match" | Some r -> r req;; -- : string = "No match" - -# match Routes.match' ~target:req.target routes with None -> "No match" | Some r -> r req;; -- : string = "Received request from /user/12 to fetch id: 12" - -# match Routes.match' ~target:"/user/hello/world/" routes with None -> "No match" | Some r -> r req;; -- : string = "search for user" - -# match Routes.match' ~target:"/user/hello/world" routes with None -> "No match because of missing trailing slash" | Some r -> r req;; -- : string = "No match because of missing trailing slash" - -# let my_fancy_route () = Routes.(s "user" / int / s "add" /? nil);; -val my_fancy_route : unit -> (int -> 'a, 'a) Routes.path = - -# let print_route = Routes.sprintf @@ my_fancy_route ();; -val print_route : int -> string = - -# print_route 12;; -- : string = "/user/12/add" +open Routes + +let greet_user name id = + Printf.sprintf "Hello, %s [%d]" name id +;; + +let sum a b = + Printf.sprintf "%d" (a + b) +;; + +(* nil and trail are used to indicate the end of a route. + nil enforces that the route doesn't end with a trailing slash + as opposed to trail which enforces a final trailing slash. *) +let router = + one_of [ + s "sum" / int / int /? nil @--> sum + ; s "user" / str / int /? trail @--> greet_user + ] +;; + +let () = + match (match' router ~target:"/sum/1/2") with + | Some "3" -> () + | _ -> assert false +;; ``` -It is possible to define custom patterns that can be used for matching. +While the library comes with pattern definitions for some common used types like +int, int32, string, etc, it allows for custom patterns for user-defined types to be +used in the same manner as the pre-defined patterns. ```ocaml -# open Routes;; -# type shape = Circle | Square -type shape = Circle | Square +open Routes;; -# let shape_of_string = function "circle" -> Some Circle | "square" -> Some Square | _ -> None -val shape_of_string : string -> shape option = - -# let shape_to_string = function Circle -> "circle" | Square -> "square" -val shape_to_string : shape -> string = - -# let shape = pattern shape_to_string shape_of_string ":shape" -val shape : ('_weak1, '_weak2) path -> (shape -> '_weak1, '_weak2) path = - - -# let process_shape (s : shape) = shape_to_string s -val process_shape : shape -> string = - -# let route () = s "shape" / shape / s "create" /? nil -val route : unit -> (shape -> '_weak3, '_weak3) path = - -# sprintf (route ()) -- : shape -> string = - -# sprintf (route ()) Square -- : string = "/shape/square/create" - -# let router = one_of [ route () @--> process_shape ] -val router : string router = - -# match' ~target:"/shape/circle/create" router -- : string option = Some "circle" - -# match' ~target:"/shape/square/create" router -- : string option = Some "square" - -# match' ~target:"/shape/triangle/create" router -- : string option = None +type shape = Circle | Square -# Format.asprintf "%a" pp_path (route ()) -- : string = "/shape/:shape/create" +(* a [string -> 'a option] function is needed to define a custom pattern. + This is what's used by the library to determine whether a path param + can be successfully coerced into the type or not. *) +let shape_of_string = function + | "circle" -> Some Circle + | "square" -> Some Square + | _ -> None +;; + +(* A ['a -> string] function is also needed. This is used when using + the [sprintf] library function to serialize a route definition into + a URI target. *) +let shape_to_string = function + | Circle -> "circle" + | Square -> "square" +;; + +(* When creating a custom pattern, it is recommended to prefix + the string label with a `:`. This will ensure that when pretty printing + a route, the output looks consistent with the pretty printers defined + for the built-in patterns. *) +let shape = pattern shape_to_string shape_of_string ":shape" + +let process_shape s = shape_to_string s + +let route () = s "shape" / shape / s "create" /? nil + +let router = one_of [ route () @--> process_shape ] + +let () = + match' ~target:"/shape/circle/create" router with + | Some "circle" -> () + | _ -> assert false ``` +More example of library usage can be seen in the [examples](./example) folder, +and as part of the [test](./test/routing_test.ml) definition. + ## Installation ###### To use the version published on opam: diff --git a/dune b/dune deleted file mode 100644 index 0b30429..0000000 --- a/dune +++ /dev/null @@ -1,9 +0,0 @@ -(alias - (name runtest) - (deps - (package routes) - (:x README.md)) - (action - (progn - (run ocaml-mdx test %{x}) - (diff? %{x} %{x}.corrected)))) diff --git a/routes.opam b/routes.opam index 14709dd..87bf24e 100644 --- a/routes.opam +++ b/routes.opam @@ -15,11 +15,10 @@ homepage: "https://github.com/anuragsoni/routes" doc: "https://anuragsoni.github.io/routes/" bug-reports: "https://github.com/anuragsoni/routes/issues" depends: [ - "ocaml" {>= "4.06.1"} + "ocaml" {>= "4.05.0"} "dune" "bisect_ppx" {dev & >= "2.0.0"} "alcotest" {with-test} - "mdx" {with-test} ] build: [ ["dune" "subst"] {pinned}