(* Import the StdLabels module which provides labeled versions of standard library functions *) open StdLabels (* Import the Routes library for type-safe HTTP routing *) open Routes (* Import Piaf for HTTP types like Method.t *) open Piaf (* Create a custom Map module keyed by HTTP methods (GET, POST, etc.) *) (* This allows us to organize routes by their HTTP verb *) module R = Map.Make (struct (* The key type is Piaf's Method.t (represents HTTP methods) *) type t = Method.t (* Define how to compare two HTTP methods for ordering in the map *) let compare a b = (* Convert methods to strings (e.g., "GET", "POST") *) let a_str = Method.to_string a in let b_str = Method.to_string b in (* Use standard string comparison *) String.compare a_str b_str ;; end) (* Define all routes in the application *) let routes = (* Use fold_left to build up a map of routes *) List.fold_left (* For each (verb, route) pair, add the route to the map under that verb *) ~f:(fun acc (v, r) -> R.add_to_list v r acc) (* Start with an empty map *) ~init:R.empty (* List of (HTTP method, route pattern -> handler) tuples *) [ `GET, (s "posts" /? nil) @--> Handler.get_posts (* `GET - HTTP GET method *) (* s "posts" - matches the literal string "posts" *) (* /? - path concatenation operator *) (* nil - end of path (no more segments) *) (* @--> - binds the route pattern to the handler function *) (* Handler.get_posts - the function that handles this route *) ; `GET, (s "posts" / int /? nil) @--> Handler.get_post (* / int - captures an integer parameter (post ID) *) ; `GET, (s "comments" / int /? nil) @--> Handler.get_comment (* Get a single comment by ID *) ; `GET, (s "users" / str / s "comments" /? nil) @--> Handler.get_user_comments (* / str - captures a string parameter (username) *) ; `GET, (s "posts" / int / s "comments" /? nil) @--> Handler.get_post_comments (* Get all comments for a specific post *) ; `GET, (s "comments" / int / s "children" /? nil) @--> Handler.get_comment_children (* Get child comments (replies) for a comment *) ; `POST, (s "posts" /? nil) @--> Post_handlers.create_post (* Create a new post *) ; `POST, (s "comments" /? nil) @--> Post_handlers.create_comment (* Create a new comment *) ; `POST, (s "votes" /? nil) @--> Post_handlers.create_vote (* Create a new vote *) (* bs5 routes *) (* root *) (* ; `GET, nil @--> Html.render @@ Pages.BlogIndex.make () *) ; `GET, nil @--> Html.render_conn @@ Pages.BlogIndex.make ; `GET, (s "l" /? nil) @--> Html.get_root ] ;; (* Transform the routes map by applying 'one_of' to each list of routes *) (* 'one_of' combines multiple routes into a single router that tries each in order *) let router = R.map one_of routes (* Function to match an incoming request against our routes *) let match_route verb path = (* Try to find routes for the given HTTP verb in our map *) match R.find_opt verb router with (* If we have routes for this verb, try to match the path *) | Some router -> (* Use the Routes library's match' function to find a matching route *) (match match' router ~target:path with (* Exact match - the path matches completely *) | FullMatch r -> Some r (* Match with trailing slash - e.g., "/posts" matches "/posts/" *) | MatchWithTrailingSlash r -> Some r (* No routes matched the path *) | NoMatch -> None) (* We don't have any routes for this HTTP verb *) | None -> None ;;