package tools import ( "context" "errors" "net/http" "sort" "sync" ) type Definition struct{ Key, Name, Description string } type Tool interface { Definition() Definition StudentHandler() http.Handler } type InstructorTool interface{ InstructorHandler() http.Handler } type Runner interface{ Run(context.Context) error } var ErrNotFound = errors.New("tool not found") type Registry struct { mu sync.RWMutex tools map[string]Tool } func NewRegistry() *Registry { return &Registry{tools: make(map[string]Tool)} } func (r *Registry) Register(t Tool) error { if t == nil || t.Definition().Key == "" { return errors.New("tool key is required") } r.mu.Lock() defer r.mu.Unlock() if _, ok := r.tools[t.Definition().Key]; ok { return errors.New("duplicate tool key") } r.tools[t.Definition().Key] = t return nil } func (r *Registry) Get(key string) (Tool, error) { r.mu.RLock() defer r.mu.RUnlock() t, ok := r.tools[key] if !ok { return nil, ErrNotFound } return t, nil } func (r *Registry) List() []Tool { r.mu.RLock() defer r.mu.RUnlock() out := make([]Tool, 0, len(r.tools)) for _, t := range r.tools { out = append(out, t) } sort.Slice(out, func(i, j int) bool { return out[i].Definition().Name < out[j].Definition().Name }) return out }