package rules import ( "encoding/json" "fmt" ) // UnmarshalRule unmarshals a rule from JSON based on its structure func UnmarshalRule(data []byte) (Rule, error) { // Unmarshal to map to check for key presence var ruleMap map[string]any if err := json.Unmarshal(data, &ruleMap); err != nil { return nil, fmt.Errorf("failed to unmarshal rule: %w", err) } // Determine rule type based on presence of keys if _, hasAnd := ruleMap["and"]; hasAnd { // AND rule - need to handle Rules field separately since it's an interface andRulesRaw, ok := ruleMap["and"].([]any) if !ok { return nil, fmt.Errorf("and rule: children must be an array") } andRule := AndRule{ Rules: make([]Rule, len(andRulesRaw)), } // Recursively unmarshal child rules for i, childRaw := range andRulesRaw { childBytes, err := json.Marshal(childRaw) if err != nil { return nil, fmt.Errorf("failed to marshal child rule %d: %w", i, err) } childRule, err := UnmarshalRule(childBytes) if err != nil { return nil, fmt.Errorf("failed to unmarshal child rule %d: %w", i, err) } andRule.Rules[i] = childRule } // Unmarshal archivers separately (JSON key is still "extractors" for backward compatibility) if extractorsRaw, ok := ruleMap["extractors"]; ok { extractorsBytes, err := json.Marshal(extractorsRaw) if err != nil { return nil, fmt.Errorf("failed to marshal archivers: %w", err) } if err := json.Unmarshal(extractorsBytes, &andRule.Archivers); err != nil { return nil, fmt.Errorf("failed to unmarshal archivers: %w", err) } } return &andRule, nil } if _, hasOr := ruleMap["or"]; hasOr { // OR rule - need to handle Rules field separately since it's an interface orRulesRaw, ok := ruleMap["or"].([]any) if !ok { return nil, fmt.Errorf("or rule: children must be an array") } orRule := OrRule{ Rules: make([]Rule, len(orRulesRaw)), } // Recursively unmarshal child rules for i, childRaw := range orRulesRaw { childBytes, err := json.Marshal(childRaw) if err != nil { return nil, fmt.Errorf("failed to marshal child rule %d: %w", i, err) } childRule, err := UnmarshalRule(childBytes) if err != nil { return nil, fmt.Errorf("failed to unmarshal child rule %d: %w", i, err) } orRule.Rules[i] = childRule } // Unmarshal archivers separately (JSON key is still "extractors" for backward compatibility) if extractorsRaw, ok := ruleMap["extractors"]; ok { extractorsBytes, err := json.Marshal(extractorsRaw) if err != nil { return nil, fmt.Errorf("failed to marshal archivers: %w", err) } if err := json.Unmarshal(extractorsBytes, &orRule.Archivers); err != nil { return nil, fmt.Errorf("failed to unmarshal archivers: %w", err) } } return &orRule, nil } if _, hasMimetype := ruleMap["mimetype"]; hasMimetype { // Mimetype rule var mimetypeRule MimetypeRule if err := json.Unmarshal(data, &mimetypeRule); err != nil { return nil, fmt.Errorf("failed to unmarshal mimetype rule: %w", err) } return &mimetypeRule, nil } if _, hasHostname := ruleMap["hostname"]; hasHostname { // Hostname rule var hostnameRule HostnameRule if err := json.Unmarshal(data, &hostnameRule); err != nil { return nil, fmt.Errorf("failed to unmarshal hostname rule: %w", err) } return &hostnameRule, nil } return nil, fmt.Errorf("unable to determine rule type: must have one of 'and', 'or', 'mimetype', or 'hostname' field") } // UnmarshalRuleConfig unmarshals a RuleConfig from JSON func UnmarshalRuleConfig(data []byte) (*RuleConfig, error) { var config struct { Rules []json.RawMessage `json:"rules"` DefaultExtractors []ArchiverConfig `json:"default_extractors,omitempty"` // JSON key kept for backward compatibility } if err := json.Unmarshal(data, &config); err != nil { return nil, fmt.Errorf("failed to unmarshal rule config: %w", err) } rules := make([]Rule, len(config.Rules)) for i, ruleData := range config.Rules { rule, err := UnmarshalRule(ruleData) if err != nil { return nil, fmt.Errorf("failed to unmarshal rule %d: %w", i, err) } rules[i] = rule } return &RuleConfig{ Rules: rules, DefaultArchivers: config.DefaultExtractors, }, nil }