Program.fs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. module App.App
  2. open State
  3. open Router
  4. open ExecNodes
  5. open Middleware
  6. open Microsoft.AspNetCore.Http
  7. open Microsoft.AspNetCore.Hosting
  8. open Microsoft.AspNetCore
  9. open System
  10. open TemplateViewEngine
  11. open Npgsql
  12. open Dapper
  13. open System.Collections.Generic
  14. [<CLIMutable>][<Struct>]
  15. type JsonStructMessage = { message : string }
  16. [<CLIMutable>]
  17. type Fortune = { id: int; message: string }
  18. [<Literal>]
  19. let ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;NoResetOnClose=true;Enlist=false;Max Auto Prepare=3"
  20. module Simple =
  21. let textFn<'T> (text:string) =
  22. let bytes = System.Text.Encoding.UTF8.GetBytes(text)
  23. let contentLength = Microsoft.Extensions.Primitives.StringValues(bytes.Length.ToString())
  24. let contentType = Microsoft.Extensions.Primitives.StringValues "text/plain"
  25. fun (x:State<'T>) ->
  26. x.HttpContext.Response.Headers.["Content-Length"] <- contentLength
  27. x.HttpContext.Response.Headers.["Content-Type"] <- contentType
  28. let t = x.HttpContext.Response.Body.WriteAsync(bytes,0,bytes.Length)
  29. let awt = t.GetAwaiter()
  30. x.CurrentState <- MachineState.Complete
  31. awt.OnCompleted x.Continue
  32. let inline jsonFn<'T> =
  33. let contentType = Microsoft.Extensions.Primitives.StringValues "application/json"
  34. fun (x:State<'T>) ->
  35. let bytes = Utf8Json.JsonSerializer.Serialize< ^a>({ message = "Hello, World!" })
  36. let contentLength = Microsoft.Extensions.Primitives.StringValues(bytes.Length.ToString())
  37. x.HttpContext.Response.Headers.["Content-Length"] <- contentLength
  38. x.HttpContext.Response.Headers.["Content-Type"] <- contentType
  39. let t = x.HttpContext.Response.Body.WriteAsync(bytes,0,bytes.Length)
  40. let awt = t.GetAwaiter()
  41. x.CurrentState <- MachineState.Complete
  42. awt.OnCompleted x.Continue
  43. module View =
  44. let fortuneView =
  45. html [] [
  46. head [] [
  47. title [] [ rawText "Fortunes" ]
  48. ]
  49. body [] [
  50. table [] [
  51. tr [] [
  52. th [] [ rawText "id" ]
  53. th [] [ rawText "message" ]
  54. ]
  55. bindFor<_,_> (fun ls -> ls :> seq<Fortune> ) (
  56. tr [] [
  57. td [] [ bindInt (fun v -> v.id) ]
  58. td [] [ bindStr (fun v -> v.message) ]
  59. ]
  60. )
  61. ]
  62. ]
  63. ] |> compileDoc
  64. let extra = { id = 0; message = "Additional fortune added at request time." }
  65. let FortuneComparer = { new IComparer<Fortune> with
  66. member self.Compare(a,b) = String.CompareOrdinal(a.message, b.message)
  67. }
  68. [<EntryPoint>]
  69. let main args =
  70. // Defualt implimentation
  71. let fallback : Zapp<_> = (fun ctx -> ctx {
  72. text "Url Not Found"
  73. status 404
  74. })
  75. let webapp =
  76. router [
  77. get "/plaintext" => fun ctx -> ctx { text "Hello, World!" }
  78. get "/json" => fun ctx -> ctx { json {JsonStructMessage.message = "Hello, World!"} }
  79. get "/fortunes" => fun ctx -> ctx {
  80. use conn = new NpgsqlConnection(ConnectionString)
  81. let! (data : Fortune seq) = conn.QueryAsync<Fortune>("SELECT id, message FROM fortune")
  82. let fortunes =
  83. let xs = data.AsList()
  84. xs.Add View.extra
  85. xs.Sort View.FortuneComparer
  86. xs
  87. ctx.Render( fortunes, View.fortuneView )
  88. }
  89. ]
  90. // Simple implimentation
  91. let plaintextPrint = Simple.textFn "Hello, World!"
  92. let jsonPrint = Simple.jsonFn<_>
  93. let notFound = Simple.textFn "Not Found"
  94. let inline simpleApp (ctx:State<_>) =
  95. match ctx.HttpContext.Request.Path.Value with
  96. | "/plaintext" -> plaintextPrint ctx
  97. | "/json" -> jsonPrint ctx
  98. | _ -> notFound ctx
  99. // Config to used based on console arg
  100. let config : Action<Builder.IApplicationBuilder> =
  101. match args with
  102. | [|"simple"|] ->
  103. printfn "Using Simple Config..."
  104. Action<Builder.IApplicationBuilder>( fun app -> app.UseZebraSimpleMiddleware<int>(0,simpleApp) |> ignore )
  105. | _ ->
  106. printfn "Using Stock Config..."
  107. Action<Builder.IApplicationBuilder>( fun app -> app.UseZebraMiddleware<int>(0,fallback,webapp) |> ignore )
  108. WebHostBuilder()
  109. .UseKestrel()
  110. .Configure(config)
  111. .Build()
  112. .Run()
  113. 0