Packages
oaspec
0.5.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.0
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.1
0.58.0
0.57.0
0.56.0
0.55.0
0.54.0
0.53.0
0.52.0
0.51.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.3
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.1.3
Generate Gleam code from OpenAPI 3.x specifications
Current section
Files
Jump to
Current section
Files
src/oaspec/codegen/middleware.gleam
import oaspec/codegen/context.{type Context, type GeneratedFile, GeneratedFile}
import oaspec/util/string_extra as se
/// Generate middleware module.
pub fn generate(ctx: Context) -> List(GeneratedFile) {
let content = generate_middleware(ctx)
[
GeneratedFile(
path: "middleware.gleam",
content: content,
target: context.SharedTarget,
),
]
}
/// Generate the middleware types and utilities.
fn generate_middleware(ctx: Context) -> String {
let sb =
se.file_header(context.version)
|> se.imports([])
let sb =
sb
|> se.doc_comment(
"A handler is a function that takes a request and returns a result.",
)
|> se.line("pub type Handler(req, res) =")
|> se.indent(1, "fn(req) -> Result(res, MiddlewareError)")
|> se.blank_line()
let sb =
sb
|> se.doc_comment(
"A middleware wraps a handler to add cross-cutting concerns.",
)
|> se.doc_comment(
"Middleware is composable: each middleware takes a handler and returns",
)
|> se.doc_comment("a new handler with added behavior.")
|> se.line("pub type Middleware(req, res) =")
|> se.indent(1, "fn(Handler(req, res)) -> Handler(req, res)")
|> se.blank_line()
// Error type
let sb =
sb
|> se.doc_comment("Errors that middleware can produce.")
|> se.line("pub type MiddlewareError {")
|> se.indent(1, "Unauthorized(detail: String)")
|> se.indent(1, "Forbidden(detail: String)")
|> se.indent(1, "BadRequest(detail: String)")
|> se.indent(1, "InternalError(detail: String)")
|> se.indent(1, "CustomError(code: Int, detail: String)")
|> se.line("}")
|> se.blank_line()
// Identity middleware
let sb =
sb
|> se.doc_comment(
"Identity middleware that passes the request through unchanged.",
)
|> se.line("pub fn identity() -> Middleware(req, res) {")
|> se.indent(1, "fn(handler: Handler(req, res)) -> Handler(req, res) {")
|> se.indent(2, "handler")
|> se.indent(1, "}")
|> se.line("}")
|> se.blank_line()
// Compose middleware
let sb =
sb
|> se.doc_comment("Compose two middleware functions.")
|> se.doc_comment("The first middleware is applied first (outermost),")
|> se.doc_comment("the second middleware is applied second (innermost).")
|> se.line("pub fn compose(")
|> se.indent(1, "first: Middleware(req, res),")
|> se.indent(1, "second: Middleware(req, res),")
|> se.line(") -> Middleware(req, res) {")
|> se.indent(1, "fn(handler: Handler(req, res)) -> Handler(req, res) {")
|> se.indent(2, "first(second(handler))")
|> se.indent(1, "}")
|> se.line("}")
|> se.blank_line()
// Apply middleware chain
let sb =
sb
|> se.doc_comment("Apply a list of middleware to a handler.")
|> se.doc_comment(
"Middleware is applied in order: first in list = outermost.",
)
|> se.line("pub fn apply(")
|> se.indent(1, "middlewares: List(Middleware(req, res)),")
|> se.indent(1, "handler: Handler(req, res),")
|> se.line(") -> Handler(req, res) {")
|> se.indent(1, "case middlewares {")
|> se.indent(2, "[] -> handler")
|> se.indent(2, "[mw, ..rest] -> mw(apply(rest, handler))")
|> se.indent(1, "}")
|> se.line("}")
|> se.blank_line()
// Logging middleware example
let sb =
sb
|> se.doc_comment("Example: A logging middleware stub.")
|> se.doc_comment("Replace the body with your logging implementation.")
|> se.line("pub fn logging() -> Middleware(req, res) {")
|> se.indent(1, "fn(handler: Handler(req, res)) -> Handler(req, res) {")
|> se.indent(2, "fn(request: req) -> Result(res, MiddlewareError) {")
|> se.indent(3, "// TODO: Add logging before/after the handler call")
|> se.indent(3, "handler(request)")
|> se.indent(2, "}")
|> se.indent(1, "}")
|> se.line("}")
|> se.blank_line()
// Retry middleware for client
let sb =
sb
|> se.doc_comment("Retry middleware for client operations.")
|> se.doc_comment("Retries the handler up to max_retries times on failure.")
|> se.line("pub fn retry(max_retries: Int) -> Middleware(req, res) {")
|> se.indent(1, "fn(handler: Handler(req, res)) -> Handler(req, res) {")
|> se.indent(2, "fn(request: req) -> Result(res, MiddlewareError) {")
|> se.indent(3, "do_retry(handler, request, max_retries, 0)")
|> se.indent(2, "}")
|> se.indent(1, "}")
|> se.line("}")
|> se.blank_line()
let sb =
sb
|> se.line("fn do_retry(")
|> se.indent(1, "handler: Handler(req, res),")
|> se.indent(1, "request: req,")
|> se.indent(1, "max_retries: Int,")
|> se.indent(1, "attempt: Int,")
|> se.line(") -> Result(res, MiddlewareError) {")
|> se.indent(1, "case handler(request) {")
|> se.indent(2, "Ok(response) -> Ok(response)")
|> se.indent(2, "Error(err) -> {")
|> se.indent(3, "case attempt < max_retries {")
|> se.indent(
4,
"True -> do_retry(handler, request, max_retries, attempt + 1)",
)
|> se.indent(4, "False -> Error(err)")
|> se.indent(3, "}")
|> se.indent(2, "}")
|> se.indent(1, "}")
|> se.line("}")
|> se.blank_line()
let _ = ctx
se.to_string(sb)
}