package cmd import ( "errors" "fmt" "reflect" "strings" ) // Проверка пути на пустое значение // Принимает на вход экземпляр bodyOutput и возвращает флаг, означающий непустой путь func (o CSVOutput) Enabled() bool { return strings.TrimSpace(o.CSVPath) != "" } // Проверка пути на пустое значение // Принимает на вход экземпляр bodyOutput и возвращает флаг, означающий непустой путь func (o JSONOutput) Enabled() bool { return strings.TrimSpace(o.JSONPath) != "" } // Проверка пути на пустое значение. // Принимает на вход экземпляр bodyOutput и возвращает флаг, означающий непустой путь func (o BodyOutput) Enabled() bool { return strings.TrimSpace(o.BodyPath) != "" } // Валидация флагов // Принимает на вход указатель на экземпляр mftParseCmd и возвращает ошибку func (c *MFTParseCmd) Validate() error { if !c.CSVOutput.Enabled() && !c.JSONOutput.Enabled() && !c.BodyOutput.Enabled() { return fmt.Errorf("mft parse requires at least one output: --csv, --json, or --body") // Проверка на наличие флага формата файла вывода } if c.BodyOutput.Enabled() && strings.TrimSpace(c.DriveLetter) == "" { return fmt.Errorf("--drive-letter is required when using --body") // Проверка наличия буквы диска в Bodyfile } if c.FileList && !c.CSVOutput.Enabled() { return fmt.Errorf("--file-list requires --csv") // Проверка CSV вывода для листинга файлов } if (c.DumpResidentFiles || c.IncludeResidentData) && !c.CSVOutput.Enabled() && !c.JSONOutput.Enabled() { return fmt.Errorf("--dump-resident-files and --include-resident-data require --csv or --json") // Проверка CSV или JSON вывода для резидентных файлов } if c.ResidentMaxBytes <= 0 || c.ResidentMaxBytes > MaxResidentDataBytes { return fmt.Errorf("--resident-max-bytes must be between 1 and %d", MaxResidentDataBytes) // Проверка максимального значения размера резидентных файлов } return nil } // Валидация флагов // Принимает на вход указатель на экземпляр mftShowCmd и возвращает ошибку func (c *MFTShowCmd) Validate() error { return nil } // Валидация флагов // Принимает на вход указатель на экземпляр mftExportRecordCmd и возвращает ошибку func (c *MFTExportRecordCmd) Validate() error { return nil } // Валидация флагов // Принимает на вход указатель на экземпляр journalParseCmd и возвращает ошибку func (c *JournalParseCmd) Validate() error { if !c.CSVOutput.Enabled() && !c.JSONOutput.Enabled() { return fmt.Errorf("j parse requires at least one output: --csv or --json") // Проверка вывода } return nil } // Валидация флагов // Принимает на вход указатель на экземпляр bootParseCmd и возвращает ошибку func (c *BootParseCmd) Validate() error { return nil } // Валидация флагов // Принимает на вход указатель на экземпляр sdsParseCmd и возвращает ошибку func (c *SDSParseCmd) Validate() error { if !c.CSVOutput.Enabled() { return fmt.Errorf("sds parse requires --csv") // Проверка вывода в CSV } return nil } // Валидация флагов // Принимает на вход указатель на экземпляр sdsShowCmd и возвращает ошибку func (c *SDSShowCmd) Validate() error { return nil } // Валидация флагов // Принимает на вход указатель на экземпляр i30ParseCmd и возвращает ошибку func (c *I30ParseCmd) Validate() error { if !c.CSVOutput.Enabled() { return fmt.Errorf("i30 parse requires --csv") // Проверка вывода в CSV } return nil } // Валидация флагов. // Принимает на вход указатель на экземпляр logFileParseCmd и возвращает ошибку func (c *LogFileParseCmd) Validate() error { return nil } // Валидация команд. // Принимает на вход объект и при наличии метода Validate производит валидацию func ValidateLeaf(cmd any) error { switch v := cmd.(type) { case interface{ Validate() error }: return v.Validate() default: return nil } } // Валидация путей. // Принимает на вход объект и при наличии метода Enabled производит валидацию func ValidatePath(cmd any) error { switch v := cmd.(type) { case interface{ Enabled() error }: return v.Enabled() default: return nil } } // Проверяет, что у команды с output-блоками включён хотя бы один вывод. func ValidateOutput(cmd any) error { if cmd == nil { return nil } v := reflect.ValueOf(cmd) if v.Kind() == reflect.Pointer { if v.IsNil() { return nil } v = v.Elem() } if v.Kind() != reflect.Struct { return nil } var hasOutputBlocks bool var hasEnabledOutput bool for i := 0; i < v.NumField(); i++ { field := v.Field(i) fieldType := v.Type().Field(i) switch fieldType.Type { case reflect.TypeOf(CSVOutput{}): hasOutputBlocks = true if enabled, ok := callEnabled(field); ok && enabled { hasEnabledOutput = true } case reflect.TypeOf(JSONOutput{}): hasOutputBlocks = true if enabled, ok := callEnabled(field); ok && enabled { hasEnabledOutput = true } case reflect.TypeOf(BodyOutput{}): hasOutputBlocks = true if enabled, ok := callEnabled(field); ok && enabled { hasEnabledOutput = true } } } if hasOutputBlocks && !hasEnabledOutput { return errors.New("you must specify at least one output path: --csv, --json or --body") } return nil } // Вызов Enabled у поля // Возвращает результат Enabled() и флаг успешности поиска метода func callEnabled(v reflect.Value) (bool, bool) { if !v.IsValid() { return false, false } // Пробуем метод у самого значения if method := v.MethodByName("Enabled"); method.IsValid() { if method.Type().NumIn() == 0 && method.Type().NumOut() == 1 && method.Type().Out(0).Kind() == reflect.Bool { out := method.Call(nil) return out[0].Bool(), true } } // Пробуем метод у указателя if v.CanAddr() { if method := v.Addr().MethodByName("Enabled"); method.IsValid() { if method.Type().NumIn() == 0 && method.Type().NumOut() == 1 && method.Type().Out(0).Kind() == reflect.Bool { out := method.Call(nil) return out[0].Bool(), true } } } return false, false }