Skip to content

Instantly share code, notes, and snippets.

@dimiro1
Created July 20, 2023 19:56
Show Gist options
  • Save dimiro1/ca6a7938ab53d057d9ed4484f6af012b to your computer and use it in GitHub Desktop.
Save dimiro1/ca6a7938ab53d057d9ed4484f6af012b to your computer and use it in GitHub Desktop.
Rich value types in Golang
package main
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"log"
"strconv"
"strings"
_ "github.com/mattn/go-sqlite3"
)
// Point our point type.
type Point struct {
X int
Y int
Z int
}
// String converts the Point to a string.
func (e Point) String() string {
return fmt.Sprintf("%d:%d:%d", e.X, e.Y, e.Z)
}
// Value function is called when the data is inserted on the database.
func (e Point) Value() (driver.Value, error) {
return driver.Value(e.String()), nil
}
// Scan function will be called when the db.Scan function is called.
func (e *Point) Scan(src interface{}) error {
var (
source string
ok bool
err error
)
if source, ok = src.(string); !ok {
return errors.New("incompatible type for Point, it must be a string")
}
parts := strings.Split(source, ":")
if len(parts) != 3 {
return errors.New("incompatible type for Point, it must be formatted as x:y:z")
}
point := Point{}
point.X, err = strconv.Atoi(parts[0])
if err != nil {
return errors.New("incompatible type for Point, x must be a number")
}
point.Y, err = strconv.Atoi(parts[1])
if err != nil {
return errors.New("incompatible type for Point, y must be a number")
}
point.Z, err = strconv.Atoi(parts[2])
if err != nil {
return errors.New("incompatible type for Point, z must be a number")
}
*e = point
return nil
}
func main() {
// Getting the sqlite database
db, err := sql.Open("sqlite3", "file:foo.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Creating the points table
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS points (
id INTEGER PRIMARY KEY AUTOINCREMENT,
point TEXT
)`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec("DELETE FROM points")
if err != nil {
log.Fatal(err)
}
// Inserting two users on the database
_, err = db.Exec("INSERT INTO points (point) VALUES (?), (?)",
Point{X: 1, Y: 2, Z: 3}, Point{X: 4, Y: 5, Z: 6})
if err != nil {
log.Fatal(err)
}
// Querying points table
rows, err := db.Query("SELECT * FROM points")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int64
// Using Point the conversion will be transparent
var point Point
err = rows.Scan(&id, &point)
if err != nil {
log.Println(err)
}
log.Println(point)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment