hako/internal/archival/rules/unmarshal.go

130 lines
4.2 KiB
Go

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
}