This package provides a flexible system for extracting values from various sources (like HTTP requests, maps, etc.) and converting them into the desired Go types with comprehensive error handling. It's designed to make it easier to work with dynamic data sources in a type-safe manner.
- Extensible Value Extraction: Supports extracting values from maps, HTTP request query parameters, and form data.
- Type Conversion: Convert extracted strings into specific Go types (
string,uint64,int64,float64,bool), including custom type conversions. - Direct Return Types: Provides direct return functions (
ReturnString,ReturnUint64, etc.) for performance optimization. - Error Handling: Collects and aggregates errors throughout the extraction and conversion process for robust error reporting.
- No external dependencies - only the Go standard library.
- Fast - run
go test -bench .. It's between 20-40% faster than the idiomatic struct tag + reflection. - Lighweight - Less than 300 LOC.
- Easy to read - Only 1
if err != nilfor all of your conversions.
go get github.com/Howard3/valueextractorHere are quick examples to get you started:
There are three main ways to use this library:
- References
- Direct value return objects
- Return Generics
package main import ( "fmt" "net/http" "github.com/Howard3/valueextractor" ) func main() { // Initialize the extractor with optional keys using the new fluent interface req, _ := http.NewRequest("GET", "/?id=123&name=John&age=30", nil) ex := valueextractor.Using(valueextractor.QueryExtractor{Query: req.URL.Query()}, valueextractor.WithOptionalKeys("age", "foo")) var id uint64 var name string var age uint64 // Note: 'age' is treated as an optional parameter ex.With("id", valueextractor.AsUint64(&id)) ex.With("name", valueextractor.AsString(&name)) ex.With("age", valueextractor.AsUint64(&age)) // No error if 'age' is missing if err := ex.Errors(); err != nil { fmt.Println("Error:", err) } else { fmt.Printf("Extracted values - ID: %d, Name: %s, Age: %d\n", id, name, age) } }The Using function supports a fluent interface for specifying optional keys. This is achieved using the WithOptionalKeys function, which modifies the extractor's behavior to treat certain keys as optional during value extraction. If these keys are missing from the source, their absence will not contribute to the error collection, simplifying error handling for optional data.
ex := valueextractor.Using(QueryExtractor{Query: req.URL.Query()}, valueextractor.WithOptionalKeys("age", "foo"))This approach allows you to declaratively specify which keys are optional, enhancing code readability and reducing the boilerplate associated with optional value handling.
package main import ( "fmt" "github.com/Howard3/valueextractor" ) func main() { ex := valueextractor.NewExtractor(...) // Assume ex is properly initialized if *valueextractor.ReturnString(ex, "name") != "John" { fmt.Println("Name not parsed correctly") } if *valueextractor.ReturnUint64(ex, "age") != 30 { fmt.Println("Age not parsed correctly") } }Return generics are easy to work with but have approximately the same performance as the struct+reflection approach.
package main import ( "fmt" "net/http" "github.com/Howard3/valueextractor" ) func main() { // Example: Extracting values from a query parameter req, _ := http.NewRequest("GET", "/?id=123&name=John", nil) queryExtractor := valueextractor.QueryExtractor{Query: req.URL.Query()} ex := valueextractor.Using(queryExtractor) id := valueextractor.Result(ex, "id", valueextractor.AsUint64) name := valueextractor.Result(ex, "name", valueextractor.AsString) if err := ex.Errors(); err != nil { fmt.Println("Error:", err) } else { fmt.Printf("Extracted values - ID: %d, Name: %s\n", id, name) } }The system is designed with extensibility in mind. You can extend it by implementing custom ValueExtractor interfaces or Converter functions. See the documentation for details on creating custom extractors and converters.
To extract values from a new data source, implement the ValueExtractor interface:
type CustomExtractor struct {} func (ce CustomExtractor) Get(key string) (string, error) { // Logic to extract and return the value based on the key }To support converting to a new type:
func AsCustomType(ref *CustomType) valueextractor.Converter { return func(ec *valueextractor.Extractor, value string) error { // Convert value to CustomType and assign to ref *ref = newVal // assign the new value return nil } }Errors are collected throughout the extraction and conversion process. Use the Errors method to retrieve any accumulated errors. The package defines ErrNotFound for missing keys and provides detailed error types for extract and convert errors, allowing precise error handling and diagnostics.
This project is licensed under the MIT License - see the LICENSE file for details.