Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions uuid/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# UUID Package

This package provides utilities for UUID (Universally Unique Identifier) generation and manipulation using only Go's standard library.

## Features
- Generate new UUIDs (v4)
- Parse UUID from string
- Convert UUID to string
- Validate a UUID string
- Compare two UUIDs
- Marshal/Unmarshal UUID to/from JSON
- Generate nil (zero value) UUID

## Usage Example
```go
import "yourmodule/uuid"

// Generate a new UUID (v4)
id := uuid.NewUUID()

// Convert to string
s := id.String()

// Validate a UUID string
valid := uuid.Validate(s)

// Parse from string
parsed, err := uuid.Parse(s)

// Compare UUIDs
isEqual := id.Equal(parsed)

// Nil UUID
zero := uuid.Nil()

// Marshal/Unmarshal JSON
b, _ := json.Marshal(id)
var u2 uuid.UUID
_ = json.Unmarshal(b, &u2)
```

## Testing
Run unit tests:
```sh
go test ./uuid
```
111 changes: 111 additions & 0 deletions uuid/uuid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Package uuid provides utilities for UUID generation and manipulation using Go's standard library only.
//
// Features:
// - Generate new UUIDs (v4)
// - Parse UUID from string
// - Convert UUID to string
// - Validate a UUID string
// - Compare two UUIDs
// - Marshal/Unmarshal UUID to/from JSON
// - Generate nil (zero value) UUID
//
// Usage Example:
//
// import "yourmodule/uuid"
// id := uuid.NewUUID()
// valid := uuid.Validate(id.String())
// ...
package uuid

import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"strings"
)

// UUID represents a 128-bit universally unique identifier (UUID).
type UUID [16]byte

// NewUUID generates a new random (version 4) UUID.
func NewUUID() UUID {
var u UUID
_, err := rand.Read(u[:])
if err != nil {
panic("uuid: cannot generate random UUID: " + err.Error())
}
// Set version (4) and variant bits as per RFC 4122
u[6] = (u[6] & 0x0f) | 0x40 // Version 4
u[8] = (u[8] & 0x3f) | 0x80 // Variant is 10
return u
}

// Parse parses a UUID from string (accepts canonical form only).
func Parse(s string) (UUID, error) {
var u UUID
s = strings.ToLower(s)
if len(s) != 36 {
return Nil(), errors.New("uuid: invalid length")
}
// Remove dashes
hexStr := strings.ReplaceAll(s, "-", "")
if len(hexStr) != 32 {
return Nil(), errors.New("uuid: invalid format")
}
b, err := hex.DecodeString(hexStr)
if err != nil || len(b) != 16 {
return Nil(), errors.New("uuid: invalid hex")
}
copy(u[:], b)
return u, nil
}

// String returns the canonical string representation of the UUID.
func (u UUID) String() string {
b := u[:]
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
b[0:4], b[4:6], b[6:8], b[8:10], b[10:16],
)
}

// Validate checks if a string is a valid UUID.
func Validate(s string) bool {
_, err := Parse(s)
return err == nil
}

// Equal compares two UUIDs for equality.
func (u UUID) Equal(other UUID) bool {
return u == other
}

// Nil returns a nil (zero value) UUID.
func Nil() UUID {
return UUID{}
}

// MarshalJSON implements the json.Marshaler interface.
func (u UUID) MarshalJSON() ([]byte, error) {
return json.Marshal(u.String())
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (u *UUID) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
parsed, err := Parse(s)
if err != nil {
return err
}
*u = parsed
return nil
}

// Format implements fmt.Formatter for UUID.
func (u UUID) Format(f fmt.State, c rune) {
fmt.Fprintf(f, "%s", u.String())
}
69 changes: 69 additions & 0 deletions uuid/uuid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package uuid

import (
"encoding/json"
"testing"
)

func TestNewUUID(t *testing.T) {
u := NewUUID()
if u.String() == "" || !Validate(u.String()) {
t.Errorf("NewUUID generated invalid UUID: %s", u.String())
}
}

func TestParse(t *testing.T) {
u1 := NewUUID()
u2, err := Parse(u1.String())
if err != nil {
t.Fatalf("Parse() error: %v", err)
}
if !u1.Equal(u2) {
t.Errorf("Parsed UUID does not match original")
}
}

func TestValidate(t *testing.T) {
u := NewUUID().String()
if !Validate(u) {
t.Errorf("Validate() failed for valid UUID: %s", u)
}
if Validate("not-a-uuid") {
t.Errorf("Validate() passed for invalid UUID")
}
}

func TestEqual(t *testing.T) {
u1 := NewUUID()
u2, _ := Parse(u1.String())
if !u1.Equal(u2) {
t.Errorf("Equal() failed for identical UUIDs")
}
u3 := NewUUID()
if u1.Equal(u3) {
t.Errorf("Equal() passed for different UUIDs")
}
}

func TestNil(t *testing.T) {
nilUUID := Nil()
if nilUUID.String() != "00000000-0000-0000-0000-000000000000" {
t.Errorf("Nil() did not return zero UUID")
}
}

func TestMarshalUnmarshalJSON(t *testing.T) {
u := NewUUID()
data, err := json.Marshal(u)
if err != nil {
t.Fatalf("MarshalJSON() error: %v", err)
}

var u2 UUID
if err := json.Unmarshal(data, &u2); err != nil {
t.Fatalf("UnmarshalJSON() error: %v", err)
}
if !u.Equal(u2) {
t.Errorf("UnmarshalJSON() did not produce equal UUID")
}
}
Loading