SkillAgentSearch skills...

Codegenr

Fast handlebars templates based code generator, ingesting swagger/openapi and other json/yaml documents with $refs, or graphql schema, outputs whatever you template

Install / Use

/learn @eventuallyconsultant/Codegenr
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

CodeGenR

codegenr graphical explanation

Installation

Install Rust : https://www.rust-lang.org/tools/install. And then install codegenr

cargo install codegenr

or install the development version

cargo install --git https://github.com/eventuallyconsultant/codegenr --branch dev

Documentation

codegenr documentation on docs.rs

Codegen Steps

Here is a simple folders/files Tree we're gonna use in example

|- _specs
  |- openapi.yaml
  |- ...
|- _templates
    |- rest-tests
      |- mytemplate.hbs
      |- ...
|- _rest-calls
  |- generated
    |- file1
    |- ...
flowchart LR
    L[Load] --> R{Is it?}
    R --> |$ref| L
    R[Resolve] --> |All resolved| RE
    RE[Render] --> PP
    PP[Process]

codegenr.toml

To generate your files, you need to define these parameters :

  • [section_name] : A unique name representing each section
  • source : The file.yaml with the data you want to use for the generation
  • templates : the folders containing handlebar templates (.hbs) you're using. Only one file in those folders must not be prefixed by _ and then is considered as the main template. The other ones are prefixed like _partial.hbs are considered partial templates.
  • output : The root folder where the files will be generated. All files output path write will be computed from this root.
  • custom_helpers : A place you can put .rhai file, each file is loaded as a custom helper, usable from the .hbs templates
  • intermediate : (Optional) if set, codegenr will output intermediate files for debug purpose
  • global_parameters : (Optional) Some values you want to use with the global_parameter helper.
Here is an example of a section in the codegenr.toml.
[api_section]
source = "./_specs/openapi.yaml"
templates = [ "./_templates/misc/rest-tests" ]
output = "./_rest-calls"
custom_helpers = [ "./_templates/_custom_helpers" ]
intermediate = "codegenr"
global_parameters =  { apiName = "MyFirstApi", apiRoot = "/v1/api" }

Load

The load step will read the source file and turn it to json

  • if it's a json file, it's quite easy
  • if it's a yaml file, it's not that hard
  • if it's a graphql sdl file, it's lead to some structure changes

If you look closely to example below, you can see that $ref: "#/components/schemas/GetMeResponse" refer to a specific path composed in 3 parts:

  • The # part is refering to the same document (Also: file.yaml#... would be referring to the document file.yaml)
  • /components/schemas/ is the path in the file
  • GetMeResponse is the object we're looking for, here is just a simple example with a property username which contains a description and a type.
# `some_openapi_file.yaml` example
openapi: 3.0.3
info:
  title: Example openapi
  description: "Openapi specifications"
  version: 1.0.0
servers:
  - url: http://localhost:8000
paths:
  /me:
    get:
      tags:
        - user
      summary: Get current users informations
      operationId: get_current_user
      responses:
        "200":
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GetMeResponse"
# ...
# ...
components:
  schemas:
    GetMeResponse:
      type: object
      required:
        - username
      properties:
        username:
          type: string
          description: a username/handle
          example: just_a_username

for more information : https://swagger.io/docs/specification/using-ref/

this is where the resolve step comes in the game :

Resolve

If the file contains from json references ($ref: "..."), the resolver will replace the reference by the value pointed at by the reference. If the reference point at another file, it'll be loaded (previous step). Then it will continue to resolve references in the value, and so on, recursively, until all $refs are replaced.

In this example, the loader finds a $ref in the source.yaml which is redirecting in the other.yaml, the loader will then load the other.yaml and resolve the reference that the $ref is pointing.

# source.yaml
openapi: 3.0.3
info:
  title: Example
  description: "Just an example"
  version: 1.0.0
servers:
  - url: http://localhost:8000
paths:
  /user:
    get:
      tags:
        - user
      summary: Get current users informations
      operationId: get_current_user
      responses:
        "200":
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: "other.yaml#/components/schemas/UserResponse"
# other.yaml
components:
  schemas:
    UserResponse:
      type: object
      required:
        - username
      properties:
        username:
          type: string
          description: a username/handle
          example: just_a_username
// all resolved & changed to json
{
  "openapi": "3.0.3",
  "info": {
    "title": "Example",
    "description": "Just an example",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "http://localhost:8000"
    }
  ],
  "paths": {
    "/user": {
      "get": {
        "tags": ["user"],
        "summary": "Get current users informations",
        "operationId": "get_current_user",
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "UserResponse": {
                    "type": "object",
                    "required": ["username"],
                    "properties": {
                      "username": {
                        "type": "string",
                        "description": "a username/handle",
                        "example": "just_a_username"
                      }
                    },
                    "x-fromRef": "other.yaml#/components/schemas/UserResponse",
                    "x-refName": "UserResponse"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
flowchart LR
  L[Load] --> F
  F[source.yaml] --> |All Resolved/No $ref| Rend
  F --> |$ref: other.yaml| L2
  Res[Resolve] --> F2
  F2[other.yaml] --> |No other $ref| Rend
  L2[Load] --> Res
  Rend[Render] --> PP
  PP[Process]

Finally, when all the refs are resolved and all necessary files loaded, the render and process will do their job.

Render

Here is our handlebar example file named mytemplate.hbs which is in the ./_templates/misc/rest-tests folder. The goal of this template will be to ouptut a .rest file named after the apiName, that contains ready to use one click examples from the swagger documentation. (usage with Rest Client VsCode extension)

This step will use the template folder you defined (./_templates/misc/rest-tests in the example above) to find all handlebars files (mytemplate.hbs, ...) and render the ONE .bhs main file (the one with no _underscore) using the load & resolve result as source, and the parameters defined in the global_parameters if there are some.

for more information about the handlebar syntax : https://handlebarsjs.com/guide/

{{!
In the following example, here is what the render will do :
It will define the variable `fileName` using the `snake_case` helper
and the `global parameter` named `apiName` which is "MyFirstApi" as 
we defined it in our example.

So the value set in `fileName` will be "my_first_api"
}}
{{set "fileName" (snake_case (global_parameter "apiName"))}}

{{!
Then it will reuse the `fileName` variable to output an instruction writing to a file.
Here it will be `my_first_api.generated.rest`.
(The process/processor instructions step will be explained later)
}}
### FILE {{get "fileName"}}.generated.rest

{{!
Thos 3 variables, will be used later by the `.rest` rest client extensions.
`apiRoot` is passed from the `codegenr` call arguments
}}
@host = localhost
@port = 8000
@api_root ={{global_parameter "apiRoot"}}

{{!
We're now iterating with the helper `each` for each item contained
in the "paths" section of the "openapi.yaml" file.
`#each` is a standard handlebars helper. 
Go and RTFM to know more about [handlebars syntax](https://handlebarsjs.com/guide/). 
}}
{{#each paths}}
  {{!
  We set a variable named `path` with the value from `@key`.
  The variable scope will stay until the `with_set` block helper is ended 
  The first iteration will have the value "/user" here.
  }}
  {{#with_set "path" @key}}
    {{!
    Next we'll loop over the current value of the handlebars context: `this`.
    "this" is the openapi path so it's children are http methods.
    So we set the `httpMethod`. 
    }}
    {{#each this}}
    {{#with_set "httpMethod" @key}}
        {{!
        Now we output some nice `.rest` comments, to make it nice to read.
        ex: it's `get_current_user` here
        }}
        # {{operationId}}
        # --- --- --- --- --- --- --- --- --- --- --- ---
        {{!
        Is there a request body json example
        }}
        {{#if requestBody}}
        {{#each requestBody.content}}{{#with_set "contentType" @key}}
        {{#each examples}}
        {{#no_empty_lines}}
        {{!
        We're gonna write our request using all informations we have
        such as our `httpMethod` and `path`.
        Your can see the `\` before `{{host` for example. 
        This is in order to escape the helper, it's a `.rest` variable usage, not a `handlebars` helper call
        }}
        {{get "httpMethod"}} http://\{{host}}:\{{port}}\{{api_root}}{{get "path"}} HTTP/1.1
        content-type: {{get "contentType"}}
        {{/no_empty_lines}}

        {{#each this}}
        {{json_to_str this format="json_pretty"}}
        {{
View on GitHub
GitHub Stars33
CategoryDevelopment
Updated3mo ago
Forks3

Languages

Rust

Security Score

92/100

Audited on Dec 19, 2025

No findings