Skip to content

Instantly share code, notes, and snippets.

@Lanayx
Created November 30, 2023 04:14
Show Gist options
  • Save Lanayx/18c21ea6319deb04be673e58958bca89 to your computer and use it in GitHub Desktop.
Save Lanayx/18c21ea6319deb04be673e58958bca89 to your computer and use it in GitHub Desktop.
Strongly typed routing
open Printf
open System
open System.Threading
open System.Text.RegularExpressions
type Response =
{
Code: int
Body: string
}
let matchRoute (format: PrintfFormat<'a, unit, 'b, 'b>) (handler: 'a) =
let handlerType = handler.GetType()
let handlerMethod = handlerType.GetMethods()[0]
let mutable counter = 0
let mutable replacedValue = format.Value
replacedValue <- Regex("%d").Replace(replacedValue, fun _ -> $"(?<digits{Interlocked.Increment(&counter)}>\d+)")
replacedValue <- Regex("%s").Replace(replacedValue, fun _ -> $"(?<letters{Interlocked.Increment(&counter)}>\w+)")
let regex = Regex($"^{replacedValue}$")
fun (url: string) ->
let m = regex.Match(url)
if (m.Success) then
let result =
handlerMethod.Invoke(handler, ([|
for g in m.Groups do
if (g.Index) > 0 then
if g.Name.StartsWith("digits", StringComparison.Ordinal) then
int g.Value
else
g.Value
|])) :?> 'b
Some result
else
None
let myHandler1 (p1: int) (p2: int) =
{
Code = 200
Body = sprintf "%d/%d" p1 p2
}
let myHandler2 (p1: int) (p2: string) =
{
Code = 200
Body = sprintf "%d/%s" p1 p2
}
let myHandler3 (p1: int) =
{
Code = 200
Body = sprintf "%d" p1
}
let routes =
seq {
matchRoute "abc/%d/%d" myHandler1
matchRoute "abc/%d/%s" myHandler2
matchRoute "fff/%d" myHandler3
}
let route url =
routes
|> Seq.choose (fun checkRoute -> checkRoute url)
|> Seq.tryHead
|> Option.defaultValue { Code = 404; Body = "NotFound"}
//example usage
route "abc/1/abc"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment