Packages

OpenAPI code generation for Gleam — parse specs, generate types, routes, clients, and React Query/SWR hooks

Current section

Files

Jump to
nori src nori.erl
Raw

src/nori.erl

-module(nori).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/nori.gleam").
-export([parse_json/1, parse/1, to_json/1, to_json_pretty/1, title/1, api_version/1, spec_version/1, parse_yaml/1, parse_file/1, check_capabilities/1, build_ir/1]).
-export_type([parse_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" OpenAPI library for Gleam.\n"
"\n"
" A comprehensive OpenAPI 3.1.x library providing:\n"
" - **Parse** - OpenAPI specs (JSON) into typed Gleam data\n"
" - **Build** - Construct specs programmatically with a fluent builder API\n"
" - **Validate** - Check specs against the OpenAPI standard\n"
"\n"
" ## Quick Start\n"
"\n"
" ### Parsing an OpenAPI document\n"
"\n"
" ```gleam\n"
" import nori\n"
"\n"
" pub fn main() {\n"
" let json = \"{\\\"openapi\\\": \\\"3.1.0\\\", \\\"info\\\": {\\\"title\\\": \\\"My API\\\", \\\"version\\\": \\\"1.0.0\\\"}}\"\n"
" let assert Ok(doc) = nori.parse_json(json)\n"
" io.println(\"API: \" <> doc.info.title)\n"
" }\n"
" ```\n"
"\n"
" ### Building an OpenAPI document\n"
"\n"
" ```gleam\n"
" import nori\n"
" import nori/document\n"
" import nori/paths\n"
" import nori/operation\n"
" import nori/server\n"
"\n"
" pub fn main() {\n"
" let doc =\n"
" document.v3_1_0(\"My API\", \"1.0.0\")\n"
" |> document.add_server(server.new(\"https://api.example.com\"))\n"
" |> document.add_path(\"/users\", paths.get(operation.with_id(\"listUsers\")))\n"
"\n"
" let json = nori.to_json(doc)\n"
" }\n"
" ```\n"
).
-type parse_error() :: {json_parse_error, binary()} |
{decode_error, list(gleam@dynamic@decode:decode_error())} |
{unsupported_version, binary()}.
-file("src/nori.gleam", 70).
?DOC(
" Parses an OpenAPI document from a JSON string.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let json = \"{ \\\"openapi\\\": \\\"3.1.0\\\", \\\"info\\\": { \\\"title\\\": \\\"My API\\\", \\\"version\\\": \\\"1.0.0\\\" } }\"\n"
" let assert Ok(doc) = nori.parse_json(json)\n"
" ```\n"
).
-spec parse_json(binary()) -> {ok, nori@document:document()} |
{error, parse_error()}.
parse_json(Input) ->
case gleam@json:parse(
Input,
{decoder, fun gleam@dynamic@decode:decode_dynamic/1}
) of
{error, _} ->
{error, {json_parse_error, <<"Invalid JSON"/utf8>>}};
{ok, Dyn} ->
case nori@decoder:decode_document(Dyn) of
{ok, Doc} ->
{ok, Doc};
{error, Errors} ->
{error, {decode_error, Errors}}
end
end.
-file("src/nori.gleam", 83).
?DOC(" Alias for parse_json.\n").
-spec parse(binary()) -> {ok, nori@document:document()} | {error, parse_error()}.
parse(Input) ->
parse_json(Input).
-file("src/nori.gleam", 94).
?DOC(
" Serializes an OpenAPI document to a JSON string.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let json = nori.to_json(doc)\n"
" ```\n"
).
-spec to_json(nori@document:document()) -> binary().
to_json(Doc) ->
_pipe = nori@encoder:encode_document(Doc),
gleam@json:to_string(_pipe).
-file("src/nori.gleam", 106).
?DOC(
" Serializes an OpenAPI document to a pretty-printed JSON string.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let json = nori.to_json_pretty(doc)\n"
" ```\n"
).
-spec to_json_pretty(nori@document:document()) -> binary().
to_json_pretty(Doc) ->
_pipe = nori@encoder:encode_document(Doc),
_pipe@1 = gleam_json_ffi:json_to_iodata(_pipe),
unicode:characters_to_binary(_pipe@1).
-file("src/nori.gleam", 113).
?DOC(" Gets the title from an OpenAPI document.\n").
-spec title(nori@document:document()) -> binary().
title(Doc) ->
erlang:element(2, erlang:element(3, Doc)).
-file("src/nori.gleam", 118).
?DOC(" Gets the version from an OpenAPI document's info.\n").
-spec api_version(nori@document:document()) -> binary().
api_version(Doc) ->
erlang:element(7, erlang:element(3, Doc)).
-file("src/nori.gleam", 123).
?DOC(" Gets the OpenAPI specification version as a string.\n").
-spec spec_version(nori@document:document()) -> binary().
spec_version(Doc) ->
nori@document:version_string(Doc).
-file("src/nori.gleam", 135).
?DOC(
" Parses an OpenAPI document from a YAML string.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let yaml = \"openapi: '3.1.0'\\ninfo:\\n title: My API\\n version: '1.0.0'\"\n"
" let assert Ok(doc) = nori.parse_yaml(yaml)\n"
" ```\n"
).
-spec parse_yaml(binary()) -> {ok, nori@document:document()} |
{error, parse_error()}.
parse_yaml(Input) ->
case nori@yaml:parse_yaml(Input) of
{ok, Doc} ->
{ok, Doc};
{error, {yaml_syntax_error, Msg}} ->
{error, {json_parse_error, Msg}};
{error, {yaml_decode_error, Errors}} ->
{error, {decode_error, Errors}};
{error, {file_error, _, Msg@1}} ->
{error, {json_parse_error, Msg@1}}
end.
-file("src/nori.gleam", 153).
?DOC(
" Loads and parses an OpenAPI spec file (YAML or JSON).\n"
"\n"
" Detects format by file extension (.yaml/.yml → YAML, .json → JSON).\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(doc) = nori.parse_file(\"./nori.yaml\")\n"
" ```\n"
).
-spec parse_file(binary()) -> {ok, nori@document:document()} |
{error, parse_error()}.
parse_file(Path) ->
case nori@yaml:parse_file(Path) of
{ok, Doc} ->
{ok, Doc};
{error, {yaml_syntax_error, Msg}} ->
{error, {json_parse_error, Msg}};
{error, {yaml_decode_error, Errors}} ->
{error, {decode_error, Errors}};
{error, {file_error, _, Msg@1}} ->
{error, {json_parse_error, Msg@1}}
end.
-file("src/nori.gleam", 180).
?DOC(
" Walk a parsed document and collect any OpenAPI features nori does not yet\n"
" support. Returns the document unchanged when nothing is flagged, otherwise\n"
" a list of `Issue`s sorted with blocking severity first.\n"
"\n"
" Use this before `build_ir` if you want to fail fast instead of producing\n"
" degraded codegen output.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(doc) = nori.parse_file(\"api.yaml\")\n"
" case nori.check_capabilities(doc) {\n"
" Ok(_) -> generate(doc)\n"
" Error(issues) -> {\n"
" list.each(issues, fn(i) { io.println(capability.issue_to_string(i)) })\n"
" }\n"
" }\n"
" ```\n"
).
-spec check_capabilities(nori@document:document()) -> {ok,
nori@document:document()} |
{error, list(nori@capability:issue())}.
check_capabilities(Doc) ->
nori@capability:check(Doc).
-file("src/nori.gleam", 198).
?DOC(
" Build a language-agnostic codegen intermediate representation from a\n"
" parsed document.\n"
"\n"
" `CodegenIR` is the contract between the parser and any code generator\n"
" (built-in Gleam/TypeScript, or third-party satellite packages). Walk it\n"
" directly to drive your own emitters.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(doc) = nori.parse_file(\"api.yaml\")\n"
" let codegen_ir = nori.build_ir(doc)\n"
" // pass codegen_ir to your own generator\n"
" ```\n"
).
-spec build_ir(nori@document:document()) -> nori@codegen@ir:codegen_i_r().
build_ir(Doc) ->
nori@codegen@ir_builder:build(Doc).