package persona import "fmt" // Validate performs comprehensive validation on a character. // Validation is species-aware and applies appropriate rules: // - Human: Ethnicity plausibility + consistency checks // - Humanoid: Morphology validation + consistency checks // - Android: Minimal validation (synthetic beings) func Validate(c *Character, opts ...ValidateOption) error { if c == nil { return ErrNilCharacter } cfg := defaultValidateConfig() for _, opt := range opts { opt(cfg) } var errs ValidationErrors // Basic validation if err := validateBasics(c); err != nil { errs = append(errs, ValidationError{ Message: err.Error(), Err: err, }) } // Species-specific validation switch c.Species { case SpeciesHuman: if !cfg.skipPlausibility { if violations := ValidateHumanPlausibility(c.DNA); violations != nil && !violations.Valid { for _, v := range violations.Violations { if v.Level == PlausibilityImplausible && !cfg.allowRareFeatures { errs.AddValue(v.Field, v.Value, v.Message, ErrImplausibleFeature) } else if cfg.strictMode && v.Level == PlausibilityRare { errs.AddValue(v.Field, v.Value, v.Message, ErrImplausibleFeature) } } } } case SpeciesHumanoid: if !cfg.skipMorphology { if morphErrs := ValidateHumanoidMorphology(c); morphErrs.HasErrors() { errs = append(errs, morphErrs...) } } case SpeciesAndroid: // Minimal validation for synthetic beings // Just ensure basic structure is valid default: errs.Add("species", fmt.Sprintf("unknown species type: %s", c.Species), ErrInvalidSpecies) } // Cross-attribute consistency checks if !cfg.skipConsistency && c.DNA != nil { if result := ValidateConsistency(c.DNA); !result.Valid { for _, issue := range result.Issues { if issue.Severity == "error" || cfg.strictMode { errs.Add(issue.Field1, issue.Description, ErrInconsistentAttributes) } } } } if errs.HasErrors() { return errs } return nil } // validateBasics performs basic structural validation. func validateBasics(c *Character) error { if c.ID == "" { return fmt.Errorf("character ID is required") } if !c.Species.IsValid() { return ErrInvalidSpecies } if c.DNA == nil { return ErrNilDNA } // Validate identity if c.DNA.Identity.Age < 0 || c.DNA.Identity.Age > 150 { return ErrInvalidAge } if c.DNA.Identity.Ethnicity != "" && !c.DNA.Identity.Ethnicity.IsValid() { return ErrInvalidEthnicity } if c.DNA.Identity.Gender != "" && !c.DNA.Identity.Gender.IsValid() { return ErrInvalidGender } return nil } // ValidateHuman is a convenience function for validating human characters. func ValidateHuman(c *Character, opts ...ValidateOption) error { if c == nil { return ErrNilCharacter } if c.Species != SpeciesHuman { return fmt.Errorf("expected human species, got %s", c.Species) } return Validate(c, opts...) } // ValidateHumanoid is a convenience function for validating humanoid characters. func ValidateHumanoid(c *Character, opts ...ValidateOption) error { if c == nil { return ErrNilCharacter } if c.Species != SpeciesHumanoid { return fmt.Errorf("expected humanoid species, got %s", c.Species) } return Validate(c, opts...) } // ValidateAndroid is a convenience function for validating android characters. func ValidateAndroid(c *Character, opts ...ValidateOption) error { if c == nil { return ErrNilCharacter } if c.Species != SpeciesAndroid { return fmt.Errorf("expected android species, got %s", c.Species) } return Validate(c, opts...) }