package studio import ( "context" "errors" "fmt" "github.com/stashapp/stash/pkg/models" ) var ( ErrStudioOwnAncestor = errors.New("studio cannot be an ancestor of itself") ) type NameExistsError struct { Name string } func (e *NameExistsError) Error() string { return fmt.Sprintf("studio with name '%s' already exists", e.Name) } type NameUsedByAliasError struct { Name string OtherStudio string } func (e *NameUsedByAliasError) Error() string { return fmt.Sprintf("name '%s' is used as alias for '%s'", e.Name, e.OtherStudio) } // EnsureStudioNameUnique returns an error if the studio name provided // is used as a name or alias of another existing tag. func EnsureStudioNameUnique(ctx context.Context, id int, name string, qb models.StudioQueryer) error { // ensure name is unique sameNameStudio, err := ByName(ctx, qb, name) if err != nil { return err } if sameNameStudio != nil && id != sameNameStudio.ID { return &NameExistsError{ Name: name, } } // query by alias sameNameStudio, err = ByAlias(ctx, qb, name) if err != nil { return err } if sameNameStudio != nil && id != sameNameStudio.ID { return &NameUsedByAliasError{ Name: name, OtherStudio: sameNameStudio.Name, } } return nil } func EnsureAliasesUnique(ctx context.Context, id int, aliases []string, qb models.StudioQueryer) error { for _, a := range aliases { if err := EnsureStudioNameUnique(ctx, id, a, qb); err != nil { return err } } return nil } type ValidateModifyReader interface { models.StudioGetter models.StudioQueryer models.AliasLoader } // Checks to make sure that: // 1. The studio exists locally // 2. The studio is not its own ancestor // 3. The studio's aliases are unique func ValidateModify(ctx context.Context, s models.StudioPartial, qb ValidateModifyReader) error { existing, err := qb.Find(ctx, s.ID) if err != nil { return err } if existing == nil { return fmt.Errorf("studio with id %d not found", s.ID) } newParentID := s.ParentID.Ptr() if newParentID != nil { if err := validateParent(ctx, s.ID, *newParentID, qb); err != nil { return err } } if s.Aliases != nil { if err := existing.LoadAliases(ctx, qb); err != nil { return err } effectiveAliases := s.Aliases.EffectiveValues(existing.Aliases.List()) if err := EnsureAliasesUnique(ctx, s.ID, effectiveAliases, qb); err != nil { return err } } return nil } func validateParent(ctx context.Context, studioID int, newParentID int, qb models.StudioGetter) error { if newParentID == studioID { return ErrStudioOwnAncestor } // ensure there is no cyclic dependency parentStudio, err := qb.Find(ctx, newParentID) if err != nil { return fmt.Errorf("error finding parent studio: %v", err) } if parentStudio == nil { return fmt.Errorf("studio with id %d not found", newParentID) } if parentStudio.ParentID != nil { return validateParent(ctx, studioID, *parentStudio.ParentID, qb) } return nil }