commit 85bac6b12be776274de0d6f6d63242278740bfa3 Author: Branden J Brown Date: Wed Aug 27 08:48:19 2025 -0400 initial commit diff --git a/boolean/boolean.go b/boolean/boolean.go new file mode 100644 index 0000000..ceb83f0 --- /dev/null +++ b/boolean/boolean.go @@ -0,0 +1,14 @@ +// boolean.go +package bocchi + +import "git.sunturtle.xyz/zephyr/errors-my-beloved/boolean/secret" + +var _ = map[bool]struct{}{ + false: {}, + secret.Secret == "gc25{}": {}, // ERROR: ./boolean.go:7:2: duplicate key false in map literal +} + +var _ = map[bool]struct{}{ + false: {}, + secret.Secret[:5] != "gc25{": {}, // silently succeeds, even though it evaluates to false +} diff --git a/boolean/go.mod b/boolean/go.mod new file mode 100644 index 0000000..f6bfeae --- /dev/null +++ b/boolean/go.mod @@ -0,0 +1,3 @@ +module git.sunturtle.xyz/zephyr/errors-my-beloved/boolean + +go 1.25.0 diff --git a/boolean/secret/secret.go b/boolean/secret/secret.go new file mode 100644 index 0000000..a8e8e1e --- /dev/null +++ b/boolean/secret/secret.go @@ -0,0 +1,3 @@ +package secret + +const Secret = "gc25{afR7yRQVCRDy}" diff --git a/distant/brain/brain.go b/distant/brain/brain.go new file mode 100644 index 0000000..ed47b66 --- /dev/null +++ b/distant/brain/brain.go @@ -0,0 +1,7 @@ +package brain + +type Interface interface { + Think(prefix []string) (string, error) +} + +func Think(s Interface, prompt string) (string, error) { panic("TODO") } diff --git a/distant/brain/sqlbrain/brain.go b/distant/brain/sqlbrain/brain.go new file mode 100644 index 0000000..d0ec8da --- /dev/null +++ b/distant/brain/sqlbrain/brain.go @@ -0,0 +1,3 @@ +package sqlbrain + +type Brain struct{} // SQL db diff --git a/distant/brain/types.go b/distant/brain/types.go new file mode 100644 index 0000000..3f4b66a --- /dev/null +++ b/distant/brain/types.go @@ -0,0 +1,6 @@ +package brain + +type ( + Message struct{} + Tuple struct{} +) diff --git a/distant/go.mod b/distant/go.mod new file mode 100644 index 0000000..f3001d6 --- /dev/null +++ b/distant/go.mod @@ -0,0 +1,3 @@ +module git.sunturtle.xyz/zephyr/errors-my-beloved/distant + +go 1.24.2 diff --git a/distant/go.sum b/distant/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/distant/main.go b/distant/main.go new file mode 100644 index 0000000..341f1ea --- /dev/null +++ b/distant/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "git.sunturtle.xyz/zephyr/errors-my-beloved/distant/brain" + "git.sunturtle.xyz/zephyr/errors-my-beloved/distant/brain/sqlbrain" +) + +func main() { + brain.Think(new(sqlbrain.Brain), "") +} diff --git a/generated/bocchi.sqlite3 b/generated/bocchi.sqlite3 new file mode 100644 index 0000000..874c5a4 Binary files /dev/null and b/generated/bocchi.sqlite3 differ diff --git a/generated/bocchi_test.go b/generated/bocchi_test.go new file mode 100644 index 0000000..8f74d9d --- /dev/null +++ b/generated/bocchi_test.go @@ -0,0 +1,11 @@ +package bocchi_test + +import ( + "unsafe" + + "git.sunturtle.xyz/zephyr/errors-my-beloved/generated/models" +) + +// If a compiler error happens here, then the database schema has changed, +// so you need to update unit tests. +var _ [0]struct{} = [unsafe.Sizeof(models.Bocchi{}) - 56]struct{}{} diff --git a/generated/go.mod b/generated/go.mod new file mode 100644 index 0000000..ab02eae --- /dev/null +++ b/generated/go.mod @@ -0,0 +1,3 @@ +module git.sunturtle.xyz/zephyr/errors-my-beloved/generated + +go 1.25.0 diff --git a/generated/models/bocchi.xo.go b/generated/models/bocchi.xo.go new file mode 100644 index 0000000..3fc3434 --- /dev/null +++ b/generated/models/bocchi.xo.go @@ -0,0 +1,147 @@ +// Package models contains generated code for schema 'bocchi.sqlite3'. +package models + +// Code generated by xo. DO NOT EDIT. + +import ( + "context" + "database/sql" +) + +// Bocchi represents a row from 'bocchi'. +type Bocchi struct { + ID sql.NullString `json:"id"` // id + Name sql.NullString `json:"name"` // name + Performances sql.NullInt64 `json:"performances"` // performances + // xo fields + _exists, _deleted bool +} + +// Exists returns true when the [Bocchi] exists in the database. +func (b *Bocchi) Exists() bool { + return b._exists +} + +// Deleted returns true when the [Bocchi] has been marked for deletion +// from the database. +func (b *Bocchi) Deleted() bool { + return b._deleted +} + +// Insert inserts the [Bocchi] to the database. +func (b *Bocchi) Insert(ctx context.Context, db DB) error { + switch { + case b._exists: // already exists + return logerror(&ErrInsertFailed{ErrAlreadyExists}) + case b._deleted: // deleted + return logerror(&ErrInsertFailed{ErrMarkedForDeletion}) + } + // insert (manual) + const sqlstr = `INSERT INTO bocchi (` + + `id, name, performances` + + `) VALUES (` + + `$1, $2, $3` + + `)` + // run + logf(sqlstr, b.ID, b.Name, b.Performances) + if _, err := db.ExecContext(ctx, sqlstr, b.ID, b.Name, b.Performances); err != nil { + return logerror(err) + } + // set exists + b._exists = true + return nil +} + +// Update updates a [Bocchi] in the database. +func (b *Bocchi) Update(ctx context.Context, db DB) error { + switch { + case !b._exists: // doesn't exist + return logerror(&ErrUpdateFailed{ErrDoesNotExist}) + case b._deleted: // deleted + return logerror(&ErrUpdateFailed{ErrMarkedForDeletion}) + } + // update with primary key + const sqlstr = `UPDATE bocchi SET ` + + `name = $1, performances = $2 ` + + `WHERE id = $3` + // run + logf(sqlstr, b.Name, b.Performances, b.ID) + if _, err := db.ExecContext(ctx, sqlstr, b.Name, b.Performances, b.ID); err != nil { + return logerror(err) + } + return nil +} + +// Save saves the [Bocchi] to the database. +func (b *Bocchi) Save(ctx context.Context, db DB) error { + if b.Exists() { + return b.Update(ctx, db) + } + return b.Insert(ctx, db) +} + +// Upsert performs an upsert for [Bocchi]. +func (b *Bocchi) Upsert(ctx context.Context, db DB) error { + switch { + case b._deleted: // deleted + return logerror(&ErrUpsertFailed{ErrMarkedForDeletion}) + } + // upsert + const sqlstr = `INSERT INTO bocchi (` + + `id, name, performances` + + `) VALUES (` + + `$1, $2, $3` + + `)` + + ` ON CONFLICT (id) DO ` + + `UPDATE SET ` + + `name = EXCLUDED.name, performances = EXCLUDED.performances ` + // run + logf(sqlstr, b.ID, b.Name, b.Performances) + if _, err := db.ExecContext(ctx, sqlstr, b.ID, b.Name, b.Performances); err != nil { + return logerror(err) + } + // set exists + b._exists = true + return nil +} + +// Delete deletes the [Bocchi] from the database. +func (b *Bocchi) Delete(ctx context.Context, db DB) error { + switch { + case !b._exists: // doesn't exist + return nil + case b._deleted: // deleted + return nil + } + // delete with single primary key + const sqlstr = `DELETE FROM bocchi ` + + `WHERE id = $1` + // run + logf(sqlstr, b.ID) + if _, err := db.ExecContext(ctx, sqlstr, b.ID); err != nil { + return logerror(err) + } + // set deleted + b._deleted = true + return nil +} + +// BocchiByID retrieves a row from 'bocchi' as a [Bocchi]. +// +// Generated from index 'sqlite_autoindex_bocchi_1'. +func BocchiByID(ctx context.Context, db DB, id sql.NullString) (*Bocchi, error) { + // query + const sqlstr = `SELECT ` + + `id, name, performances ` + + `FROM bocchi ` + + `WHERE id = $1` + // run + logf(sqlstr, id) + b := Bocchi{ + _exists: true, + } + if err := db.QueryRowContext(ctx, sqlstr, id).Scan(&b.ID, &b.Name, &b.Performances); err != nil { + return nil, logerror(err) + } + return &b, nil +} diff --git a/generated/models/db.xo.go b/generated/models/db.xo.go new file mode 100644 index 0000000..7867dc0 --- /dev/null +++ b/generated/models/db.xo.go @@ -0,0 +1,240 @@ +package models + +// Code generated by xo. DO NOT EDIT. + +import ( + "context" + "database/sql" + "database/sql/driver" + "fmt" + "io" + "time" +) + +var ( + // logf is used by generated code to log SQL queries. + logf = func(string, ...interface{}) {} + // errf is used by generated code to log SQL errors. + errf = func(string, ...interface{}) {} +) + +// logerror logs the error and returns it. +func logerror(err error) error { + errf("ERROR: %v", err) + return err +} + +// Logf logs a message using the package logger. +func Logf(s string, v ...interface{}) { + logf(s, v...) +} + +// SetLogger sets the package logger. Valid logger types: +// +// io.Writer +// func(string, ...interface{}) (int, error) // fmt.Printf +// func(string, ...interface{}) // log.Printf +func SetLogger(logger interface{}) { + logf = convLogger(logger) +} + +// Errorf logs an error message using the package error logger. +func Errorf(s string, v ...interface{}) { + errf(s, v...) +} + +// SetErrorLogger sets the package error logger. Valid logger types: +// +// io.Writer +// func(string, ...interface{}) (int, error) // fmt.Printf +// func(string, ...interface{}) // log.Printf +func SetErrorLogger(logger interface{}) { + errf = convLogger(logger) +} + +// convLogger converts logger to the standard logger interface. +func convLogger(logger interface{}) func(string, ...interface{}) { + switch z := logger.(type) { + case io.Writer: + return func(s string, v ...interface{}) { + fmt.Fprintf(z, s, v...) + } + case func(string, ...interface{}) (int, error): // fmt.Printf + return func(s string, v ...interface{}) { + _, _ = z(s, v...) + } + case func(string, ...interface{}): // log.Printf + return z + } + panic(fmt.Sprintf("unsupported logger type %T", logger)) +} + +// DB is the common interface for database operations that can be used with +// types from schema 'bocchi.sqlite3'. +// +// This works with both [database/sql.DB] and [database/sql.Tx]. +type DB interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +// Error is an error. +type Error string + +// Error satisfies the error interface. +func (err Error) Error() string { + return string(err) +} + +// Error values. +const ( + // ErrAlreadyExists is the already exists error. + ErrAlreadyExists Error = "already exists" + // ErrDoesNotExist is the does not exist error. + ErrDoesNotExist Error = "does not exist" + // ErrMarkedForDeletion is the marked for deletion error. + ErrMarkedForDeletion Error = "marked for deletion" +) + +// ErrInsertFailed is the insert failed error. +type ErrInsertFailed struct { + Err error +} + +// Error satisfies the error interface. +func (err *ErrInsertFailed) Error() string { + return fmt.Sprintf("insert failed: %v", err.Err) +} + +// Unwrap satisfies the unwrap interface. +func (err *ErrInsertFailed) Unwrap() error { + return err.Err +} + +// ErrUpdateFailed is the update failed error. +type ErrUpdateFailed struct { + Err error +} + +// Error satisfies the error interface. +func (err *ErrUpdateFailed) Error() string { + return fmt.Sprintf("update failed: %v", err.Err) +} + +// Unwrap satisfies the unwrap interface. +func (err *ErrUpdateFailed) Unwrap() error { + return err.Err +} + +// ErrUpsertFailed is the upsert failed error. +type ErrUpsertFailed struct { + Err error +} + +// Error satisfies the error interface. +func (err *ErrUpsertFailed) Error() string { + return fmt.Sprintf("upsert failed: %v", err.Err) +} + +// Unwrap satisfies the unwrap interface. +func (err *ErrUpsertFailed) Unwrap() error { + return err.Err +} + +// ErrInvalidTime is the invalid Time error. +type ErrInvalidTime string + +// Error satisfies the error interface. +func (err ErrInvalidTime) Error() string { + return fmt.Sprintf("invalid Time (%s)", string(err)) +} + +// Time is a SQLite3 Time that scans for the various timestamps values used by +// SQLite3 database drivers to store time.Time values. +type Time struct { + time time.Time +} + +// NewTime creates a time. +func NewTime(t time.Time) Time { + return Time{time: t} +} + +// String satisfies the fmt.Stringer interface. +func (t Time) String() string { + return t.time.String() +} + +// Format formats the time. +func (t Time) Format(layout string) string { + return t.time.Format(layout) +} + +// Time returns a time.Time. +func (t Time) Time() time.Time { + return t.time +} + +// Value satisfies the sql/driver.Valuer interface. +func (t Time) Value() (driver.Value, error) { + return t.time, nil +} + +// Scan satisfies the sql.Scanner interface. +func (t *Time) Scan(v interface{}) error { + switch x := v.(type) { + case time.Time: + t.time = x + return nil + case []byte: + return t.Parse(string(x)) + case string: + return t.Parse(x) + } + return ErrInvalidTime(fmt.Sprintf("%T", v)) +} + +// Parse attempts to Parse string s to t. +func (t *Time) Parse(s string) error { + if s == "" { + return nil + } + for _, f := range TimestampFormats { + if z, err := time.Parse(f, s); err == nil { + t.time = z + return nil + } + } + return ErrInvalidTime(s) +} + +// MarshalJSON satisfies the [json.Marshaler] interface. +func (t Time) MarshalJSON() ([]byte, error) { + return t.time.MarshalJSON() +} + +// UnmarshalJSON satisfies the [json.Unmarshaler] interface. +func (t *Time) UnmarshalJSON(data []byte) error { + return t.time.UnmarshalJSON(data) +} + +// TimestampFormats are the timestamp formats used by SQLite3 database drivers +// to store a time.Time in SQLite3. +// +// The first format in the slice will be used when saving time values into the +// database. When parsing a string from a timestamp or datetime column, the +// formats are tried in order. +var TimestampFormats = []string{ + // By default, use timestamps with the timezone they have. When parsed, + // they will be returned with the same timezone. + "2006-01-02 15:04:05.999999999-07:00", + "2006-01-02T15:04:05.999999999-07:00", + "2006-01-02 15:04:05.999999999", + "2006-01-02T15:04:05.999999999", + "2006-01-02 15:04:05", + "2006-01-02T15:04:05", + "2006-01-02 15:04", + "2006-01-02T15:04", + "2006-01-02", +} diff --git a/generated/schema.sql b/generated/schema.sql new file mode 100644 index 0000000..7f64b7c --- /dev/null +++ b/generated/schema.sql @@ -0,0 +1,5 @@ +CREATE TABLE bocchi ( + id TEXT PRIMARY KEY, + name TEXT, + performances INTEGER +); \ No newline at end of file diff --git a/go.work b/go.work new file mode 100644 index 0000000..ec057f8 --- /dev/null +++ b/go.work @@ -0,0 +1,10 @@ +go 1.25.0 + +use ( + ./boolean + ./distant + ./generated + ./iface + ./sizeofstring + ./stringer +) diff --git a/iface/01-iface.go b/iface/01-iface.go new file mode 100644 index 0000000..4bb327b --- /dev/null +++ b/iface/01-iface.go @@ -0,0 +1,11 @@ +package main + +type Strummer interface { + Strum() +} + +type Bocchi struct { + Guitar string +} + +var _ Strummer = (*Bocchi)(nil) diff --git a/iface/go.mod b/iface/go.mod new file mode 100644 index 0000000..28ddb23 --- /dev/null +++ b/iface/go.mod @@ -0,0 +1,3 @@ +module git.sunturtle.xyz/zephyr/errors-my-beloved/iface + +go 1.24.2 diff --git a/sizeofstring/go.mod b/sizeofstring/go.mod new file mode 100644 index 0000000..8a0657c --- /dev/null +++ b/sizeofstring/go.mod @@ -0,0 +1,3 @@ +module git.sunturtle.xyz/zephyr/errors-my-beloved/sizeofstring + +go 1.25.0 diff --git a/sizeofstring/step1.go b/sizeofstring/step1.go new file mode 100644 index 0000000..efbb4c6 --- /dev/null +++ b/sizeofstring/step1.go @@ -0,0 +1,13 @@ +//go:build ignore + +// step1.go +package bocchi + +import "unsafe" + +type Bocchi struct { + x uint32 + y uint64 +} + +var _ [0]struct{} = [unsafe.Sizeof(Bocchi{}) - 0]struct{}{} diff --git a/sizeofstring/step2.go b/sizeofstring/step2.go new file mode 100644 index 0000000..c78caaa --- /dev/null +++ b/sizeofstring/step2.go @@ -0,0 +1,13 @@ +//go:build ignore + +// step2.go +package bocchi + +import "unsafe" + +type Bocchi struct { + x uint32 + y uint64 +} + +var _ [0]struct{} = [unsafe.Sizeof(Bocchi{}) - 16]struct{}{} diff --git a/sizeofstring/step3.go b/sizeofstring/step3.go new file mode 100644 index 0000000..caefc5b --- /dev/null +++ b/sizeofstring/step3.go @@ -0,0 +1,18 @@ +package bocchi + +import "unsafe" + +type Bocchi struct { + x uint32 + y uint64 +} + +// step3.go +var ( + // assert Bocchi.x doesn't move + _ [0]struct{} = [unsafe.Offsetof(Bocchi{}.x) - 0]struct{}{} + // assert Bocchi.y comes next after Bocchi.x + _ [0]struct{} = [unsafe.Offsetof(Bocchi{}.y) - (unsafe.Alignof(Bocchi{}.y))]struct{}{} + // assert y is the last field in Bocchi + _ [0]struct{} = [unsafe.Sizeof(Bocchi{}) - (unsafe.Alignof(Bocchi{}.y) + unsafe.Sizeof(Bocchi{}.y))]struct{}{} +) diff --git a/stringer/go.mod b/stringer/go.mod new file mode 100644 index 0000000..7f4e44b --- /dev/null +++ b/stringer/go.mod @@ -0,0 +1,3 @@ +module git.sunturtle.xyz/zephyr/errors-my-beloved/stringer + +go 1.24.2 diff --git a/stringer/kessoku/band.go b/stringer/kessoku/band.go new file mode 100644 index 0000000..78e372e --- /dev/null +++ b/stringer/kessoku/band.go @@ -0,0 +1,14 @@ +package kessoku + +type BandMember int + +const ( + Bocchi BandMember = iota + Ryo + Nijika + Kita + + maxBand +) + +var _ [0]struct{} = [maxBand - 4]struct{}{} diff --git a/stringer/kessoku/bandmember_string.go b/stringer/kessoku/bandmember_string.go new file mode 100644 index 0000000..5c04007 --- /dev/null +++ b/stringer/kessoku/bandmember_string.go @@ -0,0 +1,27 @@ +// Code generated by "stringer -type BandMember ./03-stringer/kessoku/"; DO NOT EDIT. + +package kessoku + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Bocchi-0] + _ = x[Ryo-1] + _ = x[Nijika-2] + _ = x[Kita-3] + _ = x[maxBand-4] +} + +const _BandMember_name = "BocchiRyoNijikaKitamaxBand" + +var _BandMember_index = [...]uint8{0, 6, 9, 15, 19, 26} + +func (i BandMember) String() string { + if i < 0 || i >= BandMember(len(_BandMember_index)-1) { + return "BandMember(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _BandMember_name[_BandMember_index[i]:_BandMember_index[i+1]] +}