validate static method

ValidationResult validate(
  1. String commandName,
  2. Map<String, dynamic> commandData
)

Validates that a command doesn't mix incompatible properties

Critical rules:

  1. Cannot have both 'script' AND 'switch' at the same level
  2. Cannot have both 'params' AND 'switch' at the same level
  3. If 'default' is a string reference, it must point to an existing switch
  4. Switch names must be valid identifiers

Implementation

static ValidationResult validate(String commandName, Map<String, dynamic> commandData) {
  final hasScript = commandData.containsKey('script');
  final hasSwitch = commandData.containsKey('switch');
  final hasParams = commandData.containsKey('params');

  // Rule 1: Script + Switch conflict
  if (hasScript && hasSwitch) {
    return ValidationResult.error(
      'Cannot use both $bold${red}script$reset and $bold${red}switch$reset at the same time',
      hint: 'Move your script content into a \'default\' switch case',
    );
  }

  // Rule 2: Params + Switch conflict
  if (hasParams && hasSwitch) {
    return ValidationResult.error(
      'Cannot use \'params\' and \'switch\' at the same level in command: $commandName',
      hint: 'Parameters should be defined within individual switch cases, not at the switch level',
    );
  }

  // Rule 3: Validate default reference if it exists
  if (hasSwitch && commandData['switch'] is Map) {
    final switches = commandData['switch'] as Map<String, dynamic>;
    final defaultValue = switches['default'];

    if (defaultValue is String) {
      // Default references another switch by name
      if (!switches.containsKey(defaultValue)) {
        return ValidationResult.error(
          'Default switch \'$defaultValue\' does not exist in command: $commandName',
          hint: 'Available switches: ${switches.keys.where((k) => k != 'default').join(', ')}',
        );
      }

      // Check for self-reference
      if (defaultValue == 'default') {
        return ValidationResult.error(
          'Default switch cannot reference itself in command: $commandName',
          hint: 'Point default to an existing switch name or define default as a command',
        );
      }
    }

    // Rule 4: Validate switch names
    for (final switchName in switches.keys) {
      if (switchName == 'default') continue; // 'default' is allowed reserved word

      if (!_isValidSwitchName(switchName)) {
        return ValidationResult.error(
          'Invalid switch name \'$switchName\' in command: $commandName',
          hint: 'Switch names must be valid identifiers (letters, numbers, hyphens, underscores)',
        );
      }
    }

    // Recursively validate nested switches
    for (final entry in switches.entries) {
      if (entry.key == 'default' && entry.value is String) continue;

      final switchData = entry.value;
      if (switchData is Map<String, dynamic>) {
        final nestedResult = validate('$commandName > ${entry.key}', switchData);
        if (!nestedResult.isValid) {
          return nestedResult;
        }
      }
    }
  }

  return ValidationResult.success();
}