package pipeline import ( "fmt" "sort" "strings" ) type ModuleStage string const ( StageInput ModuleStage = "input" StageChunk ModuleStage = "chunk" StageExtract ModuleStage = "extract" StageMerge ModuleStage = "merge" StageNormalize ModuleStage = "normalize" StageValidate ModuleStage = "validate" StageOutput ModuleStage = "output" ) type ModuleSpec struct { Key string Stage ModuleStage Provides []string Requires []string } func defaultModuleSpec(key string, stage ModuleStage) ModuleSpec { return ModuleSpec{ Key: key, Stage: stage, } } func normalizeModuleSpec(spec ModuleSpec) ModuleSpec { return ModuleSpec{ Key: strings.TrimSpace(spec.Key), Stage: spec.Stage, Provides: normalizeCapabilities(spec.Provides), Requires: normalizeCapabilities(spec.Requires), } } func normalizeCapabilities(values []string) []string { if len(values) == 0 { return nil } seen := make(map[string]struct{}, len(values)) for _, value := range values { normalized := strings.TrimSpace(value) if normalized == "" { continue } seen[normalized] = struct{}{} } if len(seen) == 0 { return nil } capabilities := make([]string, 0, len(seen)) for value := range seen { capabilities = append(capabilities, value) } sort.Strings(capabilities) return capabilities } func cloneModuleSpec(spec ModuleSpec) ModuleSpec { return ModuleSpec{ Key: spec.Key, Stage: spec.Stage, Provides: append([]string(nil), spec.Provides...), Requires: append([]string(nil), spec.Requires...), } } func validateModuleSpec(kind string, expectedStage ModuleStage, spec ModuleSpec) error { if spec.Key == "" { return fmt.Errorf("%s key must not be empty", kind) } if spec.Stage != expectedStage { return fmt.Errorf("%s %q must use %q stage, got %q", kind, spec.Key, expectedStage, spec.Stage) } return nil } func sortedRegistryKeys[C any](constructors map[string]C) []string { if len(constructors) == 0 { return nil } keys := make([]string, 0, len(constructors)) for key := range constructors { keys = append(keys, key) } sort.Strings(keys) return keys }