feat: indexer con validacion en dos pasadas y CLI con output de errores
Reescribe el indexer con estrategia de dos pasadas: 1. Parsea todos los .md y construye sets de IDs conocidos 2. Valida integridad contra IDs conocidos, inserta solo los validos El CLI ahora muestra INVALID para errores de validacion y ERROR para errores de insercion, separando claramente ambos. Añade test de integracion que verifica que entradas invalidas se rechazan sin afectar a las validas. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+54
-40
@@ -9,13 +9,16 @@ import (
|
||||
|
||||
// IndexResult holds stats from an indexing run.
|
||||
type IndexResult struct {
|
||||
Functions int
|
||||
Types int
|
||||
Errors []string
|
||||
Functions int
|
||||
Types int
|
||||
ValidationErrors []string
|
||||
Errors []string
|
||||
}
|
||||
|
||||
// Index walks the registry root, parses all .md files, and populates the database.
|
||||
// It purges existing data first to ensure a clean rebuild.
|
||||
// Index walks the registry root, parses all .md files, validates integrity,
|
||||
// and populates the database. It uses two passes:
|
||||
// 1. Parse all entries and collect known IDs
|
||||
// 2. Validate references against known IDs, then insert valid entries
|
||||
func Index(db *DB, root string) (*IndexResult, error) {
|
||||
if err := db.Purge(); err != nil {
|
||||
return nil, fmt.Errorf("purging database: %w", err)
|
||||
@@ -23,64 +26,75 @@ func Index(db *DB, root string) (*IndexResult, error) {
|
||||
|
||||
result := &IndexResult{}
|
||||
|
||||
// Index functions
|
||||
// Pass 1: parse everything
|
||||
var functions []*Function
|
||||
var types []*Type
|
||||
|
||||
functionsDir := filepath.Join(root, "functions")
|
||||
if _, err := os.Stat(functionsDir); err == nil {
|
||||
err := filepath.Walk(functionsDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
filepath.Walk(functionsDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() || !strings.HasSuffix(path, ".md") {
|
||||
return nil
|
||||
}
|
||||
if info.IsDir() || !strings.HasSuffix(path, ".md") {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := ParseFunctionMD(path)
|
||||
if err != nil {
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("%s: %v", path, err))
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("parse %s: %v", path, err))
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := db.InsertFunction(f); err != nil {
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("insert %s: %v", f.ID, err))
|
||||
return nil
|
||||
}
|
||||
|
||||
result.Functions++
|
||||
functions = append(functions, f)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("walking functions: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Index types
|
||||
typesDir := filepath.Join(root, "types")
|
||||
if _, err := os.Stat(typesDir); err == nil {
|
||||
err := filepath.Walk(typesDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
filepath.Walk(typesDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() || !strings.HasSuffix(path, ".md") {
|
||||
return nil
|
||||
}
|
||||
if info.IsDir() || !strings.HasSuffix(path, ".md") {
|
||||
return nil
|
||||
}
|
||||
|
||||
t, err := ParseTypeMD(path)
|
||||
if err != nil {
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("%s: %v", path, err))
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("parse %s: %v", path, err))
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := db.InsertType(t); err != nil {
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("insert %s: %v", t.ID, err))
|
||||
return nil
|
||||
}
|
||||
|
||||
result.Types++
|
||||
types = append(types, t)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("walking types: %w", err)
|
||||
}
|
||||
|
||||
// Build known ID sets
|
||||
knownFunctions := make(map[string]bool, len(functions))
|
||||
for _, f := range functions {
|
||||
knownFunctions[f.ID] = true
|
||||
}
|
||||
knownTypes := make(map[string]bool, len(types))
|
||||
for _, t := range types {
|
||||
knownTypes[t.ID] = true
|
||||
}
|
||||
|
||||
// Pass 2: validate and insert
|
||||
for _, t := range types {
|
||||
if verr := ValidateType(t, knownTypes); verr != nil {
|
||||
result.ValidationErrors = append(result.ValidationErrors, verr.Error())
|
||||
continue
|
||||
}
|
||||
if err := db.InsertType(t); err != nil {
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("insert %s: %v", t.ID, err))
|
||||
continue
|
||||
}
|
||||
result.Types++
|
||||
}
|
||||
|
||||
for _, f := range functions {
|
||||
if verr := ValidateFunction(f, knownFunctions, knownTypes); verr != nil {
|
||||
result.ValidationErrors = append(result.ValidationErrors, verr.Error())
|
||||
continue
|
||||
}
|
||||
if err := db.InsertFunction(f); err != nil {
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("insert %s: %v", f.ID, err))
|
||||
continue
|
||||
}
|
||||
result.Functions++
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
Reference in New Issue
Block a user