emojid

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 6, 2026 License: MIT Imports: 6 Imported by: 0

README ยถ

Emoji-d

Have you ever looked at an int, squinted at a UUID, and thought: this primary key has absolutely no theatrical value?

Good news. The database did not ask for dignity.

emojid is a tiny Go package for using curated emoji strings as PostgreSQL IDs. It wraps a private string, validates that the value is made only from a small approved emoji alphabet, and implements the boring interfaces required to work with database/sql, JSON, text encoding, and sqlc.

This is not a UUID replacement. This is a small act of database whimsy with just enough engineering discipline to avoid becoming a production incident with merch.

What It Does

emojid.ID is a non-null, text-backed ID type for people who believe ๐Ÿฑ deserves the same referential integrity as 42.

It is valid when:

  • it is not empty
  • it is valid UTF-8
  • every rune is in the curated emoji alphabet

That means these are valid:

๐Ÿฑ
๐Ÿฆ†
๐Ÿฆ†๐Ÿฆ†
๐ŸŒฎ๐Ÿ”ฅ
๐Ÿ•๐Ÿข๐Ÿ‘€

And these are not:

hello
123
๐Ÿฑabc
๐Ÿ‡บ๐Ÿ‡ธ
๐Ÿ‘๐Ÿฝ

The package intentionally does not support flags, skin-tone variants, zero-width-joiner emoji, or whatever Unicode machinery is required to make a family emoji hold hands consistently across three operating systems and a terminal emulator.

Why

Because sometimes you need to remember software can still be fun, and sometimes that means telling PostgreSQL that ๐Ÿฆ† is a serious business identifier.

Also, this is a surprisingly decent little learning project:

  • custom Go domain types
  • database/sql.Scanner
  • driver.Valuer
  • JSON marshaling
  • text marshaling
  • UTF-8 validation
  • PostgreSQL domains
  • sqlc type overrides
  • letting the database enforce uniqueness like a responsible adult

AI Disclosure

This is not a vibe-coded project.

The core implementation was written deliberately, by a human, with tests and docs getting some AI assistance. AI also served as the project's sentient rubber duck: patient, occasionally useful, and strangely willing to discuss whether ๐ŸŒฎ is a better primary key digit than โญ.

Database Shape

Recommended PostgreSQL setup:

create domain emoji_id as text;

create table objects (
  id emoji_id primary key,
  body text not null
);

The domain is still backed by text, so PostgreSQL can index it, compare it, join on it, and reject duplicate primary keys without needing to understand the bit.

sqlc Usage

Use normal SQL:

-- name: CreateObject :one
insert into objects (id, body)
values ($1, $2)
returning id, body;

Then configure sqlc to map the database type to your Go type:

overrides:
  - db_type: "emoji_id"
    go_type:
      import: "github.com/you/emojid"
      type: "ID"

Now generated code can accept and return emojid.ID directly.

The caller brings the ID:

CreateObject(ctx, emojid.Must("๐Ÿฑ"), "hello world")

If ๐Ÿฑ is already taken, PostgreSQL rejects the insert. As it should. There can only be one cat. The database has spoken.

Interfaces

ID is designed to satisfy:

database/sql.Scanner
database/sql/driver.Valuer
encoding.TextMarshaler
encoding.TextUnmarshaler
encoding/json.Marshaler
encoding/json.Unmarshaler
fmt.Stringer

The important behavior:

  • database values scan into ID
  • query args bind as text
  • JSON encodes as "๐Ÿฑ", not {"ID":"๐Ÿฑ"}
  • text encoding uses the raw emoji string
  • invalid zero values should not quietly sneak into the database

Curated Alphabet

Phase 1 uses exactly 32 supported emoji. Think of it as base-32, except the digits have attitude and one of them is cheese.

package emojid

// validRunes is a curated list of emojis that are allowed for use in IDs.
// Source: https://unicode.org/emoji/charts/full-emoji-list.html
var validRunes = map[rune]struct{}{
	'๐Ÿ˜€': {},
	'๐Ÿš€': {},
	'๐Ÿ”ฅ': {},
	'๐Ÿ•': {},
	'๐Ÿ˜…': {},
	'๐Ÿ˜': {},
	'๐Ÿ™„': {},
	'๐Ÿคฎ': {},
	'๐Ÿค ': {},
	'๐ŸŒฎ': {},
	//10
	'๐Ÿ˜Ž': {},
	'๐Ÿ’ฉ': {},
	'๐Ÿ‘ป': {},
	'๐Ÿ˜บ': {},
	'๐Ÿ': {},
	'๐Ÿ’ฏ': {},
	'๐Ÿ–•': {},
	'๐Ÿ‘': {},
	'๐Ÿ’ช': {},
	'๐Ÿ‘€': {},
	//20
	'๐Ÿฆ†': {},
	'๐Ÿฆ‰': {},
	'๐Ÿ”': {},
	'๐Ÿธ': {},
	'๐Ÿข': {},
	'๐ŸŒ': {},
	'๐ŸŒญ': {},
	'๐ŸŒฒ': {},
	'๐Ÿ„': {},
	'๐ŸŒ': {},
	//30
	'๐Ÿ†': {},
	'๐Ÿง€': {},
}

Capacity, if you want to pretend this is a numbering system:

1 emoji  = 32 possible IDs
2 emoji  = 1,024 possible IDs
3 emoji  = 32,768 possible IDs
4 emoji  = 1,048,576 possible IDs
5 emoji  = 33,554,432 possible IDs
6 emoji  = 1,073,741,824 possible IDs

But Phase 1 does not generate IDs. You pick them. The database tells you when your vibes collide. This is called architecture.

Non-Goals

This package does not try to:

  • generate IDs
  • guarantee uniqueness before insert
  • support nullable IDs
  • support every Unicode emoji
  • support skin tones or flags
  • define semantic emoji ordering
  • replace migrations, query builders, or your sense of judgment

Range queries may work because PostgreSQL can compare text and nobody can stop you:

where id between $1 and $2

But that means PostgreSQL text ordering, not โ€œall emoji between duck and taco in the grand taxonomy of nonsense.โ€ Please do not cite this package in court.

Philosophy

The database owns uniqueness.

The caller owns choosing the ID.

emojid owns one tiny invariant:

This string is made only of approved emoji runes.

That is enough. Anything more would risk turning a joke into enterprise software, and nobody needs an Emoji Identity Governance Committee.

Documentation ยถ

Index ยถ

Constants ยถ

This section is empty.

Variables ยถ

This section is empty.

Functions ยถ

func Validate ยถ

func Validate(value string) error

Validate checks if the given string is a valid emoji ID.

Types ยถ

type ID ยถ

type ID struct {
	// contains filtered or unexported fields
}

ID represents a unique identifier validated against a curated list of emojis.

func Must ยถ

func Must(value string) ID

Must creates a new ID from a string value. It panics if the value is not a valid emoji ID.

func New ยถ

func New(value string) (ID, error)

New creates a new ID from a string value. It returns an error if the value is not a valid emoji ID.

func (*ID) IsValid ยถ

func (i *ID) IsValid() bool

IsValid returns true if the ID has a non-empty value.

func (ID) MarshalJSON ยถ

func (i ID) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface. It returns the JSON encoding of the ID.

func (ID) MarshalText ยถ

func (i ID) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface. It returns the text representation of the ID.

func (*ID) Scan ยถ

func (i *ID) Scan(value any) error

Scan implements the sql.Scanner interface. It validates that the value is a string or []byte and matches the curated emoji list.

func (ID) String ยถ

func (i ID) String() string

String returns the string representation of the ID.

func (*ID) UnmarshalJSON ยถ

func (i *ID) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface. It validates the JSON-encoded data and stores the result in the ID.

func (*ID) UnmarshalText ยถ

func (i *ID) UnmarshalText(text []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface. It validates the text and stores the result in the ID.

func (ID) Value ยถ

func (i ID) Value() (driver.Value, error)

Value implements the driver.Valuer interface. It returns the string value or an error if the value is not defined.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL