package db import ( "errors" "fmt" "github.com/mattn/go-sqlite3" ) var ( ErrNotInDB = errors.New("not in db") ErrItemIsDeleted = errors.New("item is deleted") ErrForeignKeyViolation = errors.New("foreign key constraint failed") ErrDatabaseAlreadyExists = errors.New("target already exists") ) type ForeignKey interface { ~uint64 | ~string } type ForeignKeyError[T ForeignKey] struct { Field string TargetTable string FkValue T } func NewForeignKeyError[T ForeignKey](field, table string, fkValue T) ForeignKeyError[T] { return ForeignKeyError[T]{ Field: field, TargetTable: table, FkValue: fkValue, } } func (e ForeignKeyError[T]) Error() string { return fmt.Sprintf(`%s: fk field %q (to %q) with value "%v"`, ErrForeignKeyViolation.Error(), e.Field, e.TargetTable, e.FkValue, ) } // Unwrap returns the sentinel error-- permits the use of errors.Is for convenience func (e ForeignKeyError[T]) Unwrap() error { return ErrForeignKeyViolation } // ------------- // SQLite errors // ------------- // IsSqliteFkError checks whether an error is a SQLite foreign key constraint violation. func IsSqliteFkError(err error) bool { var sqliteErr sqlite3.Error if !errors.As(err, &sqliteErr) { return false } return errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintForeignKey) } // IsSqliteUniqError checks whether an error is a SQLite unique constraint violation. func IsSqliteUniqError(err error) bool { var sqliteErr sqlite3.Error if !errors.As(err, &sqliteErr) { return false } return errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintUnique) } // IsSqliteNotNullError checks whether an error is a SQLite not null constraint violation. func IsSqliteNotNullError(err error) bool { var sqliteErr sqlite3.Error if !errors.As(err, &sqliteErr) { return false } return errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintNotNull) }