// Package userresolution defines the domain result returned by the user // resolution boundary before session creation. package userresolution import ( "errors" "fmt" "strings" "galaxy/authsession/internal/domain/common" ) // Kind identifies the coarse user-resolution result for one normalized e-mail. type Kind string const ( // KindExisting reports that the e-mail belongs to an existing user. KindExisting Kind = "existing" // KindCreatable reports that the e-mail is free and user creation is // allowed. KindCreatable Kind = "creatable" // KindBlocked reports that the e-mail or subject is blocked from login or // registration. KindBlocked Kind = "blocked" ) // IsKnown reports whether Kind is one of the user-resolution kinds supported // by the current domain model. func (k Kind) IsKnown() bool { switch k { case KindExisting, KindCreatable, KindBlocked: return true default: return false } } // BlockReasonCode stores one machine-readable user-block reason. type BlockReasonCode string // String returns BlockReasonCode as its stored code value. func (code BlockReasonCode) String() string { return string(code) } // IsZero reports whether BlockReasonCode is empty. func (code BlockReasonCode) IsZero() bool { return strings.TrimSpace(string(code)) == "" } // Validate reports whether BlockReasonCode is non-empty and normalized for // domain use. func (code BlockReasonCode) Validate() error { switch { case code.IsZero(): return errors.New("block reason code must not be empty") case strings.TrimSpace(string(code)) != string(code): return errors.New("block reason code must not contain surrounding whitespace") default: return nil } } // Result stores the coarse user-resolution outcome consumed by later auth // workflow stages. type Result struct { // Kind reports the coarse resolution outcome. Kind Kind // UserID is set only when Kind is KindExisting. UserID common.UserID // BlockReasonCode is set only when Kind is KindBlocked. BlockReasonCode BlockReasonCode } // Validate reports whether Result satisfies the Stage-2 structural invariants. func (r Result) Validate() error { if !r.Kind.IsKnown() { return fmt.Errorf("user resolution kind %q is unsupported", r.Kind) } switch r.Kind { case KindExisting: if err := r.UserID.Validate(); err != nil { return fmt.Errorf("user resolution user id: %w", err) } if !r.BlockReasonCode.IsZero() { return errors.New("existing user resolution must not contain block reason code") } case KindCreatable: if !r.UserID.IsZero() { return errors.New("creatable user resolution must not contain user id") } if !r.BlockReasonCode.IsZero() { return errors.New("creatable user resolution must not contain block reason code") } case KindBlocked: if !r.UserID.IsZero() { return errors.New("blocked user resolution must not contain user id") } if err := r.BlockReasonCode.Validate(); err != nil { return fmt.Errorf("user resolution block reason code: %w", err) } } return nil }