diff --git a/cmd/core/cmd/docgen.go b/cmd/core/cmd/docgen.go deleted file mode 100644 index 465e3b3f..00000000 --- a/cmd/core/cmd/docgen.go +++ /dev/null @@ -1,185 +0,0 @@ -package cmd - -import ( - "bytes" - "fmt" - "go/ast" - "go/doc" - "go/parser" - "go/token" - "os" - "path/filepath" - "strings" - "text/template" - - "github.com/leaanthony/clir" -) - -func AddDocGenCommand(parent *clir.Command) { - cmd := parent.NewSubCommand("docgen", "Generates Markdown documentation for the public API of the services.") - cmd.Action(func() error { - return runDocGen() - }) -} - -func runDocGen() error { - const pkgDir = "pkg" - const outDir = "docs/services" - - if err := os.MkdirAll(outDir, 0755); err != nil { - return fmt.Errorf("failed to create output directory: %w", err) - } - - dirs, err := os.ReadDir(pkgDir) - if err != nil { - return fmt.Errorf("failed to read pkg directory: %w", err) - } - - for _, dir := range dirs { - if !dir.IsDir() { - continue - } - serviceName := dir.Name() - servicePath := filepath.Join(pkgDir, serviceName) - - if err := generateDocsForService(servicePath, serviceName, outDir); err != nil { - fmt.Printf("Warning: Could not generate docs for service '%s': %v\n", serviceName, err) - } - } - - fmt.Println("Documentation generated successfully in", outDir) - return nil -} - -func generateDocsForService(servicePath, serviceName, outDir string) error { - fset := token.NewFileSet() - filter := func(info os.FileInfo) bool { - return !strings.HasSuffix(info.Name(), "_test.go") - } - pkgs, err := parser.ParseDir(fset, servicePath, filter, parser.ParseComments) - if err != nil { - return fmt.Errorf("failed to parse directory %s: %w", servicePath, err) - } - - internalPath := filepath.Join(servicePath, "internal") - if _, err := os.Stat(internalPath); err == nil { - pkgs, err = parser.ParseDir(fset, internalPath, nil, parser.ParseComments) - if err != nil { - return fmt.Errorf("failed to parse internal directory %s: %w", internalPath, err) - } - } - - var pkg *ast.Package - for _, p := range pkgs { - pkg = p - break - } - if pkg == nil { - return fmt.Errorf("no package found in %s", servicePath) - } - - docPkg := doc.New(pkg, "./", doc.AllDecls) - - md, err := generateMarkdown(docPkg) - if err != nil { - return fmt.Errorf("failed to generate markdown: %w", err) - } - - outFile := filepath.Join(outDir, serviceName+".md") - return os.WriteFile(outFile, []byte(md), 0644) -} - -const docTemplate = `--- -title: {{ .Name }} ---- -# Service: ` + "`" + `{{ .Name }}` + "`" + ` - -{{ .Doc }} - -{{if .Consts}} -## Constants -{{range .Consts}} -` + "```go" + ` -{{- range .Names }}{{ . }}{{ end }} -` + "```" + ` -{{ .Doc }} -{{end}}{{end}} - -{{if .Types}} -## Types -{{range .Types}} -### ` + "`" + `type {{ .Name }}` + "`" + ` -` + "```go" + ` -type {{ .Name }} {{.Decl | formatNode}} -` + "```" + ` -{{ .Doc }} - -{{if .Methods}} -#### Methods -{{range .Methods}} -- ` + "`" + `{{ .Name }}({{ .Decl.Type.Params | formatParams }}) {{ .Decl.Type.Results | formatParams }}` + "`" + `: {{ .Doc | oneLine }} -{{end}}{{end}} - -{{end}}{{end}} - -{{if .Funcs}} -## Functions -{{range .Funcs}} -- ` + "`" + `{{ .Name }}({{ .Decl.Type.Params | formatParams }}) {{ .Decl.Type.Results | formatParams }}` + "`" + `: {{ .Doc | oneLine }} -{{end}}{{end}} -` - -func generateMarkdown(pkg *doc.Package) (string, error) { - funcMap := template.FuncMap{ - "oneLine": func(s string) string { - return strings.TrimSpace(strings.Replace(s, "\n", " ", -1)) - }, - "formatNode": func(decl *ast.GenDecl) string { - if len(decl.Specs) == 0 { - return "" - } - spec := decl.Specs[0].(*ast.TypeSpec) - return nodeToString(spec.Type) - }, - "formatParams": func(fieldList *ast.FieldList) string { - if fieldList == nil { - return "" - } - var params []string - for _, p := range fieldList.List { - var names []string - for _, name := range p.Names { - names = append(names, name.Name) - } - typeStr := nodeToString(p.Type) - if len(names) > 0 { - params = append(params, strings.Join(names, ", ")+" "+typeStr) - } else { - params = append(params, typeStr) - } - } - return strings.Join(params, ", ") - }, - } - - tmpl, err := template.New("doc").Funcs(funcMap).Parse(docTemplate) - if err != nil { - return "", err - } - - var buf bytes.Buffer - if err := tmpl.Execute(&buf, pkg); err != nil { - return "", err - } - - return buf.String(), nil -} - -func nodeToString(node ast.Node) string { - var buf bytes.Buffer - err := ast.Fprint(&buf, token.NewFileSet(), node, nil) - if err != nil { - return "" - } - return buf.String() -} diff --git a/cmd/core/cmd/root.go b/cmd/core/cmd/root.go index 5cc58c4e..98ed2df6 100644 --- a/cmd/core/cmd/root.go +++ b/cmd/core/cmd/root.go @@ -67,9 +67,7 @@ func Execute() error { // Add the top-level commands devCmd := app.NewSubCommand("dev", "Development tools for Core Framework") AddAPICommands(devCmd) - AddTestGenCommand(devCmd) AddSyncCommand(devCmd) - AddDocGenCommand(devCmd) AddBuildCommand(app) AddTviewCommand(app) // Run the application diff --git a/cmd/core/cmd/test_gen.go b/cmd/core/cmd/test_gen.go deleted file mode 100644 index c7fc8c21..00000000 --- a/cmd/core/cmd/test_gen.go +++ /dev/null @@ -1,115 +0,0 @@ -package cmd - -import ( - "bytes" - "fmt" - "os" - "path/filepath" - "text/template" - - "github.com/leaanthony/clir" - "golang.org/x/text/cases" - "golang.org/x/text/language" -) - -// AddTestGenCommand adds the 'test-gen' command to the given parent command. -func AddTestGenCommand(parent *clir.Command) { - testGenCmd := parent.NewSubCommand("test-gen", "Generates baseline test files for public service APIs.") - testGenCmd.LongDescription("This command scans for public services and generates a standard set of API contract tests for each one.") - testGenCmd.Action(func() error { - if err := runTestGen(); err != nil { - return fmt.Errorf("Error during test generation: %w", err) - } - fmt.Println("API test files generated successfully.") - return nil - }) -} - -const testFileTemplate = `package {{.ServiceName}}_test - -import ( - "testing" - - "github.com/Snider/Core/{{.ServiceName}}" - "github.com/Snider/Core/pkg/core" -) - -// TestNew ensures that the public constructor New is available. -func TestNew(t *testing.T) { - if {{.ServiceName}}.New == nil { - t.Fatal("{{.ServiceName}}.New constructor is nil") - } - // Note: This is a basic check. Some services may require a core instance - // or other arguments. This test can be expanded as needed. -} - -// TestRegister ensures that the public factory Register is available. -func TestRegister(t *testing.T) { - if {{.ServiceName}}.Register == nil { - t.Fatal("{{.ServiceName}}.Register factory is nil") - } -} - -// TestInterfaceCompliance ensures that the public Service type correctly -// implements the public {{.InterfaceName}} interface. This is a compile-time check. -func TestInterfaceCompliance(t *testing.T) { - // This is a compile-time check. If it compiles, the test passes. - var _ core.{{.InterfaceName}} = (*{{.ServiceName}}.Service)(nil) -} -` - -func runTestGen() error { - pkgDir := "pkg" - internalDirs, err := os.ReadDir(pkgDir) - if err != nil { - return fmt.Errorf("failed to read pkg directory: %w", err) - } - - for _, dir := range internalDirs { - if !dir.IsDir() || dir.Name() == "core" { - continue - } - - serviceName := dir.Name() - publicDir := serviceName - - // Check if a corresponding top-level public API directory exists. - if _, err := os.Stat(publicDir); os.IsNotExist(err) { - continue // Not a public service, so we skip it. - } - - testFilePath := filepath.Join(publicDir, serviceName+"_test.go") - fmt.Printf("Generating test file for service '%s' at %s\n", serviceName, testFilePath) - - if err := generateTestFile(testFilePath, serviceName); err != nil { - fmt.Fprintf(os.Stderr, "Warning: could not generate test for service '%s': %v\n", serviceName, err) - } - } - - return nil -} - -func generateTestFile(path, serviceName string) error { - tmpl, err := template.New("test").Parse(testFileTemplate) - if err != nil { - return err - } - - tcaser := cases.Title(language.English) - interfaceName := tcaser.String(serviceName) - - data := struct { - ServiceName string - InterfaceName string - }{ - ServiceName: serviceName, - InterfaceName: interfaceName, - } - - var buf bytes.Buffer - if err := tmpl.Execute(&buf, data); err != nil { - return err - } - - return os.WriteFile(path, buf.Bytes(), 0644) -}