//go:build linux || freebsd // +build linux freebsd package protocol import ( "fmt" "net/url" "sort" "strings" "sync" "codeberg.org/petrbalvin/goget/internal/core" ) // Registry manages all registered protocols type Registry struct { mu sync.RWMutex protocols map[string]core.Protocol } // NewRegistry creates a new registry func NewRegistry() *Registry { return &Registry{ protocols: make(map[string]core.Protocol), } } // Register registers a protocol under its primary scheme func (r *Registry) Register(p core.Protocol) error { r.mu.Lock() defer r.mu.Unlock() scheme := p.Scheme() if scheme == "" { return fmt.Errorf("protocol scheme cannot be empty") } if _, exists := r.protocols[scheme]; exists { return fmt.Errorf("protocol already registered: %s", scheme) } r.protocols[scheme] = p return nil } // normalizeScheme normalizes a scheme for lookup (https -> http, webdavs -> webdav, etc.) func normalizeScheme(scheme string) string { scheme = strings.ToLower(scheme) switch scheme { case "https": return "http" case "webdavs": return "webdav" case "ftps": return "ftp" default: return scheme } } // Get gets a protocol for a given URL with scheme normalization func (r *Registry) Get(u *url.URL) (core.Protocol, error) { r.mu.RLock() defer r.mu.RUnlock() if u == nil { return nil, fmt.Errorf("url cannot be nil") } scheme := normalizeScheme(u.Scheme) p, exists := r.protocols[scheme] if !exists { return nil, fmt.Errorf("unsupported protocol: %s", u.Scheme) } // Additional validation via CanHandle if !p.CanHandle(u) { return nil, fmt.Errorf("protocol %s cannot handle url: %s", scheme, u.String()) } return p, nil } // List returns a list of all registered protocols func (r *Registry) List() []string { r.mu.RLock() defer r.mu.RUnlock() schemes := make([]string, 0, len(r.protocols)) for scheme := range r.protocols { schemes = append(schemes, scheme) } sort.Strings(schemes) return schemes } // Supports determines whether a protocol is supported (with normalization) func (r *Registry) Supports(scheme string) bool { r.mu.RLock() defer r.mu.RUnlock() _, exists := r.protocols[normalizeScheme(scheme)] return exists } // Capabilities returns the capabilities of all protocols func (r *Registry) Capabilities() map[string][]string { r.mu.RLock() defer r.mu.RUnlock() caps := make(map[string][]string) for scheme, p := range r.protocols { caps[scheme] = p.Capabilities() } return caps } // GlobalRegistry is the global registry instance var GlobalRegistry = NewRegistry() // Register is a helper for registering in the global registry func Register(p core.Protocol) error { return GlobalRegistry.Register(p) } // Get is a helper for getting a protocol from the global registry func Get(u *url.URL) (core.Protocol, error) { return GlobalRegistry.Get(u) }