persona-community-1/pkg/persona/validation.go
jordan 4004f88f4a
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/manual/woodpecker Pipeline was successful
Initialize project from skeleton template
2026-02-23 10:20:59 +00:00

139 lines
3.5 KiB
Go

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...)
}