validateWorkflowInputs validates that required inputs are provided and checks for typos. This validation function is co-located with the run command implementation because: - It's specific to the workflow run operation - It's only called during workflow dispatch - It provides immediate feedback bef
(markdownPath string, providedInputs []string)
| 171 | // |
| 172 | // This follows the principle that domain-specific validation belongs in domain files. |
| 173 | func validateWorkflowInputs(markdownPath string, providedInputs []string) error { |
| 174 | // Extract workflow inputs |
| 175 | workflowInputs, err := getWorkflowInputs(markdownPath) |
| 176 | if err != nil { |
| 177 | // Don't fail validation if we can't extract inputs |
| 178 | validationLog.Printf("Failed to extract workflow inputs: %v", err) |
| 179 | return nil |
| 180 | } |
| 181 | |
| 182 | // If no inputs are defined, no validation needed |
| 183 | if len(workflowInputs) == 0 { |
| 184 | return nil |
| 185 | } |
| 186 | |
| 187 | // Parse provided inputs into a map |
| 188 | providedInputsMap := make(map[string]string) |
| 189 | for _, input := range providedInputs { |
| 190 | parts := strings.SplitN(input, "=", 2) |
| 191 | if len(parts) == 2 { |
| 192 | providedInputsMap[parts[0]] = parts[1] |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | // Check for required inputs that are missing (ignore inputs with a default value) |
| 197 | var missingInputs []string |
| 198 | for inputName, inputDef := range workflowInputs { |
| 199 | if inputDef.Required && inputDef.Default == nil { |
| 200 | if _, exists := providedInputsMap[inputName]; !exists { |
| 201 | missingInputs = append(missingInputs, inputName) |
| 202 | } |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | // Check for typos in provided input names |
| 207 | var typos []string |
| 208 | var suggestions []string |
| 209 | validInputNames := slices.Collect(maps.Keys(workflowInputs)) |
| 210 | |
| 211 | for providedName := range providedInputsMap { |
| 212 | // Check if this is a valid input name |
| 213 | if _, exists := workflowInputs[providedName]; !exists { |
| 214 | // Find closest matches |
| 215 | matches := parser.FindClosestMatches(providedName, validInputNames, 3) |
| 216 | if len(matches) > 0 { |
| 217 | typos = append(typos, providedName) |
| 218 | suggestions = append(suggestions, fmt.Sprintf("'%s' -> did you mean '%s'?", providedName, strings.Join(matches, "', '"))) |
| 219 | } else { |
| 220 | typos = append(typos, providedName) |
| 221 | suggestions = append(suggestions, fmt.Sprintf("'%s' is not a valid input name", providedName)) |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | // Build error message if there are validation errors |
| 227 | if len(missingInputs) > 0 || len(typos) > 0 { |
| 228 | var errorParts []string |
| 229 | |
| 230 | if len(missingInputs) > 0 { |