bojanz / currency

Currency handling for Go.
https://pkg.go.dev/github.com/bojanz/currency
MIT License
557 stars 45 forks source link

Amount should implement driver.Valuer and sql.Scanner interfaces #9

Closed bojanz closed 3 years ago

bojanz commented 3 years ago

This would allow users on Postgres to define a composite type for prices, and scan it directly into currency.Amount.

Example composite type:

CREATE TYPE price AS (
   number NUMERIC, 
   currency_code CHAR(3)
);

Example usage in a table:

CREATE TABLE products (
   id CHAR(26) PRIMARY KEY,
   name TEXT NOT NULL,
   price price NOT NULL,
   created_at TIMESTAMPTZ NOT NULL,
   updated_at TIMESTAMPTZ
);

Example scan:

p := Product{}
row := tx.QueryRow(ctx, `SELECT id, name, price, created_at, updated_at FROM products WHERE id = $1`, id)
err := row.Scan(&p.ID, &p.Name, &p.Price, &p.CreatedAt, &p.UpdatedAt)

The two interface implementations would look something like this:

// Value implements the database/driver.Valuer interface.
func (a Amount) Value() (driver.Value, error) {
    return fmt.Sprintf("(%v,%v)", a.Number(), a.CurrencyCode()), nil
}

// Scan implements the database/sql.Scanner interface.
func (a *Amount) Scan(src interface{}) error {
    // Wire format: "(9.99,USD)".
    input := strings.Trim(src.(string), "()")
    values := strings.Split(input, ",")
        n := values[0]
        currencyCode := values[1]
        // Do something with the two strings.

    return nil
}