152 lines
4.3 KiB
Go
152 lines
4.3 KiB
Go
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
|
|
|
package types
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"path/filepath"
|
|
"log/slog"
|
|
_ "runtime/debug"
|
|
)
|
|
|
|
/*
|
|
|
|
The `types` package provides a generic method of registering a type factory.
|
|
|
|
*/
|
|
|
|
var (
|
|
ErrUnknownType = errors.New("Unknown type")
|
|
ErrInvalidProduct = errors.New("Invalid product")
|
|
)
|
|
|
|
type Factory[Product comparable] func(*url.URL) Product
|
|
type RegistryTypeMap[Product comparable] map[string]Factory[Product]
|
|
|
|
//type Name[Registry any] string //`json:"type"`
|
|
|
|
type Types[Product comparable] struct {
|
|
registry RegistryTypeMap[Product]
|
|
contentTypes RegistryTypeMap[Product]
|
|
}
|
|
|
|
func New[Product comparable]() *Types[Product] {
|
|
return &Types[Product]{registry: make(map[string]Factory[Product]), contentTypes: make(map[string]Factory[Product])}
|
|
}
|
|
|
|
func (t *Types[Product]) Register(names []string, factory Factory[Product]) {
|
|
for _,name := range names {
|
|
t.registry[name] = factory
|
|
}
|
|
}
|
|
|
|
func (t *Types[Product]) RegisterContentType(contenttypes []string, factory Factory[Product]) {
|
|
for _,name := range contenttypes {
|
|
t.contentTypes[name] = factory
|
|
}
|
|
}
|
|
|
|
func (t *Types[Product]) FromExtension(path string) (Factory[Product], error) {
|
|
elements := strings.Split(path, ".")
|
|
numberOfElements := len(elements)
|
|
slog.Info("Types[Product].FromExtension()", "path", path, "elements", elements, "types", t.contentTypes, "numberOfElements", numberOfElements)
|
|
if numberOfElements >= 2 {
|
|
// slog.Info("Types[Product].FromExtension()", "path", path, "elements", elements, "types", t.contentTypes, "stacktrace", string(debug.Stack()))
|
|
if numberOfElements > 2 {
|
|
ext := strings.Join(elements[numberOfElements - 2: numberOfElements], ".")
|
|
// slog.Info("Types[Product].FromExtension() - Lookup", "ext", ext, "stacktrace", string(debug.Stack()))
|
|
slog.Info("Types[Product].FromExtension() - Lookup", "ext", ext, "types", t)
|
|
if src := t.GetContentType(ext); src != nil {
|
|
return src, nil
|
|
}
|
|
}
|
|
slog.Info("Types[Product].FromExtension() - Lookup", "ext", elements[numberOfElements - 1], "types", t.contentTypes)
|
|
if src := t.GetContentType(elements[numberOfElements - 1]); src != nil {
|
|
return src, nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("%w: %s", ErrUnknownType, path)
|
|
}
|
|
|
|
func (t *Types[Product]) New(uri string) (result Product, err error) {
|
|
u, e := url.Parse(uri)
|
|
if u == nil || e != nil {
|
|
err = fmt.Errorf("%w: %s %s", ErrUnknownType, e, uri)
|
|
return
|
|
}
|
|
return t.NewFromParsedURI(u)
|
|
}
|
|
|
|
func (t *Types[Product]) NewFromParsedURI(u *url.URL) (result Product, err error) {
|
|
if u.Scheme == "" {
|
|
u.Scheme = "file"
|
|
}
|
|
|
|
path := filepath.Join(u.Hostname(), u.Path)
|
|
if len(path) > 0 {
|
|
if d, lookupErr := t.FromExtension(filepath.Base(path)); d != nil {
|
|
return d(u), lookupErr
|
|
} else {
|
|
slog.Info("Types[Product].NewFromParsedURI() - FromExtension()", "uri", u, "path", path, "error", lookupErr) //, "stacktrace", string(debug.Stack()))
|
|
}
|
|
}
|
|
|
|
if r, ok := t.registry[u.Scheme]; ok {
|
|
if result = r(u); result != any(nil) {
|
|
return result, nil
|
|
} else {
|
|
return result, fmt.Errorf("%w: factory failed creating %s", ErrInvalidProduct, u.String())
|
|
}
|
|
}
|
|
err = fmt.Errorf("%w: %s", ErrUnknownType, u.Scheme)
|
|
return
|
|
}
|
|
|
|
// Returns an uninitialized resource for the given type
|
|
func (t *Types[Product]) NewFromType(typename string) (result Product, err error) {
|
|
if r, ok := t.registry[typename]; ok {
|
|
if result = r(nil); result != any(nil) {
|
|
return result, nil
|
|
} else {
|
|
return result, fmt.Errorf("%w: factory failed creating %s", ErrInvalidProduct, typename)
|
|
}
|
|
}
|
|
err = fmt.Errorf("%w: %s", ErrUnknownType, typename)
|
|
return
|
|
}
|
|
|
|
func (t *Types[Product]) Has(typename string) bool {
|
|
if _, ok := t.registry[typename]; ok {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t *Types[Product]) HasContentType(contenttype string) bool {
|
|
if _, ok := t.contentTypes[contenttype]; ok {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t *Types[Product]) GetContentType(contenttype string) Factory[Product] {
|
|
if d, ok := t.contentTypes[contenttype]; ok {
|
|
return d
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
func (n *Name[Registry]) UnmarshalJSON(b []byte) error {
|
|
ProductTypeName := strings.Trim(string(b), "\"")
|
|
if Registry.Has(ProductTypeName) {
|
|
*n = Name[Registry](ProductTypeName)
|
|
return nil
|
|
}
|
|
return fmt.Errorf("%w: %s", ErrUnknownType, ProductTypeName)
|
|
}
|
|
*/
|