Skip to content

feat: add modusgraphgen code generator#10

Draft
mlwelles wants to merge 10 commits intomatthewmcneely:mainfrom
mlwelles:feature/add-modusgraphgen
Draft

feat: add modusgraphgen code generator#10
mlwelles wants to merge 10 commits intomatthewmcneely:mainfrom
mlwelles:feature/add-modusgraphgen

Conversation

@mlwelles
Copy link

@mlwelles mlwelles commented Feb 27, 2026

Summary

  • Adds cmd/modusgraphgen/, a code generation tool that reads Go structs with json/dgraph tags and produces typed CRUD clients, query builders, auto-paging iterators, functional options, and a Kong CLI
  • Zero new dependencies -- the generator uses only the Go standard library (go/ast, go/parser, text/template, embed)
  • Code organized under cmd/modusgraphgen/internal/ with model, parser, and generator packages
  • Self-contained test fixtures in testdata/ with golden file regression tests
  • Adds "Code Generation" section to README
  • Adds .osgrep, .opencode, .claude to .gitignore

Usage

//go:generate go run github.com/matthewmcneely/modusgraph/cmd/modusgraphgen

Or install directly:

go install github.com/matthewmcneely/modusgraph/cmd/modusgraphgen@latest

What Gets Generated

Output Scope
client_gen.go Typed Client with sub-clients per entity
page_options_gen.go First(n) and Offset(n) pagination
iter_gen.go Auto-paging SearchIter and ListIter iterators
<entity>_gen.go Per entity CRUD: Get, Add, Update, Delete, Search, List
<entity>_options_gen.go Functional options for each scalar field
<entity>_query_gen.go Fluent query builder with filters, ordering, pagination
cmd/<pkg>/main.go Kong CLI with subcommands per entity

Testing

All parser and generator tests pass. Existing modusGraph tests unaffected.


Summary by cubic

Adds modusgraph-gen, a standard-library code generator that reads Go structs with json/dgraph tags and generates a typed client, CRUD APIs, fluent query builders, auto-paging iterators, functional options, and an optional Kong CLI with a raw DQL query command. Also updates .gitignore and removes stray binaries/IDE files; README and golden tests updated; no new dependencies.

  • New Features

    • New generator under cmd/modusgraph-gen with internal model, parser, and generator packages.
    • Generates client_gen.go, page_options_gen.go, iter_gen.go, and per-entity CRUD, options, and query files.
    • Adds Client.QueryRaw and a generated CLI with global --addr/--dir flags and a "query" subcommand; standalone cmd/query marked deprecated.
    • Embeds templates and writes formatted *_gen.go output; updated README and docs.
  • Migration

    • Add: //go:generate go run github.com/matthewmcneely/modusgraph/cmd/modusgraph-gen
    • Run: go generate ./...
    • Optional: configure CLI output with -cli-dir and -cli-name; use the generated CLI's "query" subcommand instead of cmd/query.

Written for commit d6f4145. Summary will update on new commits.

Merge the standalone modusGraphGen code generator into the modusGraph
repository as cmd/modusgraphgen/. The tool parses Go structs with
json/dgraph tags and generates typed CRUD clients, query builders,
auto-paging iterators, functional options, and a Kong CLI.

Code is organized under cmd/modusgraphgen/internal/ with model, parser,
and generator packages. Test fixtures are self-contained in testdata/.

The generated code imports github.com/matthewmcneely/modusgraph and
requires no additional dependencies beyond the Go standard library.
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 59 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmd/modusgraphgen/internal/generator/templates/options.go.tmpl">

<violation number="1" location="cmd/modusgraphgen/internal/generator/templates/options.go.tmpl:6">
P2: time import detection only checks types starting with `time.`, so pointer/slice/map time types (e.g., `*time.Time`, `[]time.Time`) won't trigger the import and will generate code that references `time` without importing it.</violation>
</file>

<file name="cmd/modusgraphgen/internal/generator/templates/cli.go.tmpl">

<violation number="1" location="cmd/modusgraphgen/internal/generator/templates/cli.go.tmpl:68">
P2: Non-string scalar CLI inputs are ignored because only string-typed fields are copied into the entity in the generated Add command.</violation>
</file>

<file name="cmd/modusgraphgen/internal/parser/parser.go">

<violation number="1" location="cmd/modusgraphgen/internal/parser/parser.go:1">
P2: Parser iteration over Go maps (`pkgs` and `pkgAST.Files`) is unsorted, making package selection and entity ordering nondeterministic across runs. This can yield unstable or incorrect generated output when multiple packages or files are present.</violation>
</file>

<file name="cmd/modusgraphgen/internal/generator/templates/iter.go.tmpl">

<violation number="1" location="cmd/modusgraphgen/internal/generator/templates/iter.go.tmpl:3">
P2: iter.go.tmpl always emits context/iter imports even when .Entities is empty, producing an iter_gen.go with unused imports and no declarations. This can break builds for empty schemas.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

{{$name := .Entity.Name}}
{{$fields := scalarFields .Entity.Fields}}
{{- $needsTime := false}}
{{- range $fields}}{{if hasPrefix .GoType "time."}}{{$needsTime = true}}{{end}}{{end}}
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: time import detection only checks types starting with time., so pointer/slice/map time types (e.g., *time.Time, []time.Time) won't trigger the import and will generate code that references time without importing it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/modusgraphgen/internal/generator/templates/options.go.tmpl, line 6:

<comment>time import detection only checks types starting with `time.`, so pointer/slice/map time types (e.g., `*time.Time`, `[]time.Time`) won't trigger the import and will generate code that references `time` without importing it.</comment>

<file context>
@@ -0,0 +1,34 @@
+{{$name := .Entity.Name}}
+{{$fields := scalarFields .Entity.Fields}}
+{{- $needsTime := false}}
+{{- range $fields}}{{if hasPrefix .GoType "time."}}{{$needsTime = true}}{{end}}{{end}}
+{{- $extImports := externalImports $fields .Imports}}
+{{if or $needsTime (gt (len $extImports) 0)}}
</file context>
Fix with Cubic


func (c *{{.Name}}AddCmd) Run(client *{{$.Name}}.Client) error {
v := &{{$.Name}}.{{.Name}}{
{{- range scalarFields .Fields}}{{if and (not .IsUID) (not .IsDType) (eq .GoType "string")}}
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Non-string scalar CLI inputs are ignored because only string-typed fields are copied into the entity in the generated Add command.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/modusgraphgen/internal/generator/templates/cli.go.tmpl, line 68:

<comment>Non-string scalar CLI inputs are ignored because only string-typed fields are copied into the entity in the generated Add command.</comment>

<file context>
@@ -0,0 +1,129 @@
+
+func (c *{{.Name}}AddCmd) Run(client *{{$.Name}}.Client) error {
+	v := &{{$.Name}}.{{.Name}}{
+{{- range scalarFields .Fields}}{{if and (not .IsUID) (not .IsDType) (eq .GoType "string")}}
+		{{.Name}}: c.{{.Name}},
+{{- end}}{{end}}
</file context>
Fix with Cubic

@@ -0,0 +1,354 @@
// Package parser extracts entity and field metadata from Go source files by
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Parser iteration over Go maps (pkgs and pkgAST.Files) is unsorted, making package selection and entity ordering nondeterministic across runs. This can yield unstable or incorrect generated output when multiple packages or files are present.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/modusgraphgen/internal/parser/parser.go:

<comment>Parser iteration over Go maps (`pkgs` and `pkgAST.Files`) is unsorted, making package selection and entity ordering nondeterministic across runs. This can yield unstable or incorrect generated output when multiple packages or files are present.</comment>

<file context>
@@ -0,0 +1,354 @@
+// Package parser extracts entity and field metadata from Go source files by
+// inspecting struct declarations and their struct tags. It uses go/ast and
+// go/parser to walk the AST, then builds a model.Package for the generator.
+package parser
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
</file context>
Fix with Cubic

@@ -0,0 +1,63 @@
package {{.Name}}

import (
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: iter.go.tmpl always emits context/iter imports even when .Entities is empty, producing an iter_gen.go with unused imports and no declarations. This can break builds for empty schemas.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/modusgraphgen/internal/generator/templates/iter.go.tmpl, line 3:

<comment>iter.go.tmpl always emits context/iter imports even when .Entities is empty, producing an iter_gen.go with unused imports and no declarations. This can break builds for empty schemas.</comment>

<file context>
@@ -0,0 +1,63 @@
+package {{.Name}}
+
+import (
+	"context"
+	"iter"
</file context>
Fix with Cubic

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 63 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".gitignore">

<violation number="1" location=".gitignore:27">
P2: Unscoped `.gitignore` pattern `query` will ignore untracked files under existing `cmd/query`, making future additions easy to miss. Scope the ignore to the repo root if that's the intent.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

.osgrep
.opencode
.claude
query
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Unscoped .gitignore pattern query will ignore untracked files under existing cmd/query, making future additions easy to miss. Scope the ignore to the repo root if that's the intent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .gitignore, line 27:

<comment>Unscoped `.gitignore` pattern `query` will ignore untracked files under existing `cmd/query`, making future additions easy to miss. Scope the ignore to the repo root if that's the intent.</comment>

<file context>
@@ -24,3 +24,4 @@ cpu_profile.prof
 .osgrep
 .opencode
 .claude
+query
</file context>
Suggested change
query
/query
Fix with Cubic

Remove .idea/.gitignore, modusgraphgen binary, and benchmark JSON
files that were accidentally committed. Update .gitignore to prevent
recurrence.
@mlwelles mlwelles marked this pull request as draft February 27, 2026 23:22
@matthewmcneely
Copy link
Owner

@mlwelles Can you create a README in the cmd/modusgraph-gen folder that provides an overview of when/how to use this? For instance, how does it improve upon one simply creating a

type TestEntity struct {
	Name        string    `json:"name,omitempty" dgraph:"index=exact"`
	Description string    `json:"description,omitempty" dgraph:"index=term"`
	CreatedAt   time.Time `json:"createdAt,omitempty"`

	UID string `json:"uid,omitempty"`
	DType []string `json:"dgraph.type,omitempty"`
}

and then client.Insert()-ing a TestEntity?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants