Skip to content

seyallius/go-reflection-misc

Repository files navigation

Go Reflection Utilities

A Go package providing utility functions for reflection-based operations and type conversions.

Table of Contents

Overview

This package provides utilities for:

  • Converting generic interface{} / any values into specific types using reflection (inspired by Rust's TryInto / Into traits)
  • Working with struct tags and reflection for field manipulation
  • Type-safe reflection operations with compile-time safety

Installation

go get github.com/seyedali-dev/go-reflection-misc

Features

  • Type conversion from interface{} to specific types with error handling
  • Struct field manipulation using reflection
  • Struct tag parsing and manipulation
  • Type-safe reflection API
  • Zero-value checking for various Go types

Usage

Interface Utilities

TryInto

Converts a generic interface{} value to a specific type with error handling:

package main

import (
    "fmt"
    "log"
    "github.com/seyedali-dev/go-reflection-misc"
)

func main() {
    // Example 1: Conversion from compatible type
    var x any = "123"
    num, err := reflectx.TryInto[int](x)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(num) // Output: 123 (if convertType handles string→int)

    // Example 2: Nil input
    var y any = nil
    str, err := reflectx.TryInto[string](y)
    if err != nil {
        fmt.Println("Error:", err) // Output: Error: cannot convert type from nil interface
    }

    // Example 3: Direct type match
    var z any = 42
    result, err := reflectx.TryInto[int](z)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(result) // Output: 42
}

TryIntoResult

The result.Result version of TryInto:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
    "github.com/seyedali-dev/goxide/rusty/result"
)

func main() {
    var x any = "hello"
    result := reflectx.TryIntoResult[string](x)
    if result.IsOk() {
        fmt.Println(result.Unwrap()) // Output: hello
    } else {
        fmt.Println("Error:", result.Err())
    }
}

Into

The panic version of TryInto (panics on error):

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

func main() {
    var x any = 42
    result := reflectx.Into[int](x)
    fmt.Println(result) // Output: 42
}

IntoResult

The result.Result version of Into:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

func main() {
    var x any = "hello"
    result := reflectx.IntoResult[string](x)
    if result.IsOk() {
        fmt.Println(result.Unwrap()) // Output: hello
    }
}

IsEmpty

Checks if a value is the zero value for its type:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

func main() {
    fmt.Println(reflectx.IsEmpty(""))        // true (empty string)
    fmt.Println(reflectx.IsEmpty(0))         // true (zero int)
    fmt.Println(reflectx.IsEmpty([]int{}))   // true (empty slice)
    fmt.Println(reflectx.IsEmpty("hello"))   // false
    fmt.Println(reflectx.IsEmpty(42))        // false
}

Struct Reflection Utilities

FieldTagValue

Returns the value of a specific struct tag:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string `step:"profile" step_category:"employee"`
}

func main() {
    value := reflectx.FieldTagValue(User{}, "Name", "step_category", "")
    fmt.Println(value) // Output: employee
}

FieldHasTags

Checks if a field has all specified tags:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string `step:"profile" step_category:"employee"`
}

func main() {
    hasTags := reflectx.FieldHasTags(User{}, "Name", []string{"step", "step_category"})
    fmt.Println(hasTags) // Output: true
}

FieldTagKeys

Returns all tag keys for a specific field:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string `step:"profile" step_category:"employee"`
}

func main() {
    keys := reflectx.FieldTagKeys(User{}, "Name")
    fmt.Println(keys) // Output: [step step_category]
}

FieldTagKeyValue

Returns the tag key and value for a specific field:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string `step:"profile" step_category:"employee"`
}

func main() {
    key, value, found := reflectx.FieldTagKeyValue(User{}, "Name", "step", "")
    if found {
        fmt.Printf("Key: %s, Value: %s\n", key, value) // Output: Key: step, Value: profile
    }
}

Field

Returns the field struct information:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string
    Age  int
}

func main() {
    field := reflectx.Field(User{}, "Name")
    fmt.Println(field.Name)    // Output: Name
    fmt.Println(field.Type)    // Output: string
}

FieldSet

Sets a field on the given object using reflection:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string
    Age  int
}

func main() {
    user := &User{}
    err := reflectx.FieldSet(user, "Name", "John Doe")
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println(user.Name) // Output: John Doe
    }
}

FieldValue

Returns the field value and whether it's settable:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
    "reflect"
)

type User struct {
    Name string
}

func main() {
    user := &User{Name: "John"}
    value, settable := reflectx.FieldValue(user, "Name")
    if settable {
        fmt.Println(value.String()) // Output: John
    }
}

Fields

Returns all fields of a struct:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string
    Age  int
}

func main() {
    fields := reflectx.Fields(User{})
    for _, field := range fields {
        fmt.Println(field.Name, field.Type) // Output: Name string, Age int
    }
}

FieldValues

Returns the values of all fields in a struct:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string
    Age  int
}

func main() {
    user := User{Name: "John", Age: 30}
    values := reflectx.FieldValues(user)
    for _, value := range values {
        fmt.Println(value) // Output: John, 30
    }
}

FieldNameByTagValue

Returns the name of the first field where a tag has a specific value:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Field1 string `step_category:"employee"`
    Field2 string `step_category:"customer"`
}

func main() {
    fieldName := reflectx.FieldNameByTagValue(User{}, "step_category", "employee")
    fmt.Println(fieldName) // Output: Field1
}

FieldNamesByTagValue

Returns the names of all fields where a tag has a specific value:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Field1 string `step_category:"employee"`
    Field2 string `step_category:"customer"`
    Field3 string `step_category:"employee"`
}

func main() {
    fieldNames := reflectx.FieldNamesByTagValue(User{}, "step_category", "employee")
    fmt.Println(fieldNames) // Output: [Field1 Field3]
}

FieldTagValues

Returns all values for a specific struct tag key:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string `step_restrictions:"emp_only,has_code"`
}

func main() {
    values := reflectx.FieldTagValues(User{}, "Name", "step_restrictions", ",")
    fmt.Println(values) // Output: [emp_only has_code]
}

FieldHasTagValue

Checks if a field has a specific value in a multi-value tag:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string `step_restrictions:"emp_only,has_code"`
}

func main() {
    hasValue := reflectx.FieldHasTagValue(User{}, "Name", "step_restrictions", "has_code", ",")
    fmt.Println(hasValue) // Output: true
}

FieldsByTagContainsValue

Returns all fields where a tag contains a specific value:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Field1 string `step_restrictions:"emp_only,has_code"`
    Field2 string `step_restrictions:"customer"`
    Field3 string `step_restrictions:"has_code"`
}

func main() {
    fields := reflectx.FieldsByTagContainsValue(User{}, "step_restrictions", "has_code", ",")
    for _, field := range fields {
        fmt.Println(field.Name) // Output: Field1 Field3
    }
}

StructTypeName

Returns the fully-qualified name of the struct type:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct{}

func main() {
    name := reflectx.StructTypeName(User{})
    fmt.Println(name) // Output: main.User (or the package path)
}

Type-Safe Usage Pattern

For compile-time safety, you can create a reflector for a specific type:

package main

import (
    "fmt"
    "github.com/seyedali-dev/go-reflection-misc"
)

type User struct {
    Name string `step:"profile" step_category:"employee"`
}

func main() {
    // Create type-safe reflector once
    userReflector := reflectx.ForType[User]()

    // Now get compile-time safety
    tagValue := userReflector.FieldTagValue("Name", "step_category", "")
    hasTags := userReflector.FieldHasTags("Name", []string{"step", "step_category"})

    fmt.Println(tagValue) // Output: employee
    fmt.Println(hasTags)  // Output: true

    // This would cause IDE/compiler errors if fields don't exist:
    // userReflector.FieldTagValue("NonExistentField", "tag") // Compile error
}

API Reference

Interface Utilities

  • TryInto[T any](structType any) (T, error): Attempts to convert a generic value to type T with error handling
  • TryIntoResult[T any](structType any) result.Result[T]: Result version of TryInto
  • Into[T any](structType any) T: Panic version of TryInto
  • IntoResult[T any](structType any) result.Result[T]: Result version of Into
  • IsEmpty[T any](v T) bool: Checks if a value is the zero value for its type

Struct Reflection Utilities

  • FieldTagValue(structType interface{}, fieldName string, tag string, tagValSeparator string) string: Returns the value of a specific struct tag
  • FieldHasTags(structType interface{}, fieldName string, tags []string) bool: Checks if a field has all specified tags
  • FieldTagKeys(structType interface{}, fieldName string) []string: Returns all tag keys for a field
  • FieldTagKeyValue(structType interface{}, fieldName string, tagKey string, tagValSeparator string) (key string, value string, found bool): Returns the tag key and value for a field
  • Field(structType interface{}, fieldName string) reflect.StructField: Returns field struct information
  • FieldSet(structType interface{}, fieldName string, value interface{}) error: Sets a field on an object
  • FieldValue(intrfc interface{}, fieldName string) (reflect.Value, bool): Returns field value and settable status
  • Fields(structType interface{}) []reflect.StructField: Returns all fields of a struct
  • FieldValues(structInstance interface{}) []reflect.Value: Returns values of all fields in a struct
  • FieldNameByTagValue(structType interface{}, tagKey, expectedValue string) string: Returns the name of the first field with a specific tag value
  • FieldNamesByTagValue(structType interface{}, tagKey, expectedValue string) []string: Returns names of all fields with a specific tag value
  • FieldTagValues(structType interface{}, fieldName string, tagKey string, tagValSeparator string) []string: Returns all values for a specific tag key
  • FieldHasTagValue(structType interface{}, fieldName string, tagKey string, expectedValue string, tagValSeparator string) bool: Checks if a field has a specific value in a multi-value tag
  • FieldsByTagContainsValue(structType interface{}, tagKey, expectedValue, tagValSeparator string) []reflect.StructField: Returns fields where a tag contains a specific value
  • StructTypeName(structType interface{}) string: Returns the fully-qualified name of a struct type

Type-Safe Wrapper Methods

The Reflector[T] type provides type-safe versions of all struct reflection utilities with compile-time checking:

  • ForType[T any]() *Reflector[T]: Creates a type-safe reflector
  • FieldTagValue(fieldName string, tag string, tagValSeparator string) string
  • FieldHasTags(fieldName string, tags []string) bool
  • FieldTagKeys(fieldName string) []string
  • FieldTagKeyValue(fieldName string, tagKey string, tagValSeparator string) (key string, value string, found bool)
  • Field(fieldName string) reflect.StructField
  • FieldValue(instance *T, fieldName string) (reflect.Value, bool)
  • Fields() []reflect.StructField
  • FieldValues(instance *T) []reflect.Value
  • FieldNameByTagValue(tagKey, expectedValue string) string
  • FieldNamesByTagValue(tagKey, expectedValue string) []string
  • FieldTagValues(fieldName string, tagKey string, tagValSeparator string) []string
  • FieldHasTagValue(fieldName string, tagKey string, expectedValue string, tagValSeparator string) bool
  • FieldsByTagContainsValue(tagKey, expectedValue, tagValSeparator string) []reflect.StructField
  • StructTypeName() string

Benchmarks

This package includes benchmark tests for performance evaluation:

  • Interface conversion benchmarks in interface_benchmark_test.go
  • Struct reflection benchmarks in reflection_struct_benchmark_test.go

Run benchmarks with:

go test -bench=.

Testing

Run all tests with:

go test ./...

The package includes:

  • Unit tests for interface utilities in interface_unit_test.go
  • Unit tests for struct reflection utilities in reflect_struct_unit_test.go
  • End-to-end tests for struct reflection utilities in reflection_struct_e2e_test.go

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Miscellanious utilities and helper functions for go reflections.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages