// Copyright 2024 Matthew Rich . All rights reserved. package types import ( "errors" "fmt" "net/url" "strings" "path/filepath" ) /* The `types` package provides a generic method of registering a type factory. */ var ( ErrUnknownType = errors.New("Unknown type") ErrInvalidProduct = errors.New("Invalid product") ) //type Name[Registry any] string //`json:"type"` type Factory[Product comparable] func(*url.URL) Product type RegistryTypeMap[Product comparable] map[string]Factory[Product] type Types[Product comparable] struct { registry RegistryTypeMap[Product] } func New[Product comparable]() *Types[Product] { return &Types[Product]{registry: 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]) FromExtension(path string) (Factory[Product], error) { elements := strings.Split(path, ".") numberOfElements := len(elements) if numberOfElements > 2 { if src := t.Get(strings.Join(elements[numberOfElements - 2: numberOfElements - 1], ".")); src != nil { return src, nil } } if src := t.Get(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", ErrUnknownType, e) return } if u.Scheme == "" { u.Scheme = "file" } path := filepath.Join(u.Hostname(), u.Path) if d, lookupErr := t.FromExtension(path); d != nil { return d(u), lookupErr } 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, uri) } } err = fmt.Errorf("%w: %s", ErrUnknownType, u.Scheme) return } func (t *Types[Product]) Has(typename string) bool { if _, ok := t.registry[typename]; ok { return true } return false } func (t *Types[Product]) Get(typename string) Factory[Product] { if d, ok := t.registry[typename]; 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) } */