Ctrl K
ComunidadGuías…

internal/mcp/args.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package mcp

import (
	"fmt"
	"math"
	"slices"
	"strconv"
	"strings"

	"github.com/sophotechlabs/spinoza/internal/api"
)

const (
	argResource  = "resource"
	argName      = "name"
	argNamespace = "namespace"
	argGroup     = "group"
	argLimit     = "limit"
	argQuery     = "query"
	argAction    = "action"
	argYAML      = "yaml"
	argReplicas  = "replicas"
	argRevision  = "revision"
	argEngine    = "engine"
	keyError     = "error"
)

type arguments map[string]any

func (a arguments) text(key string) string {
	raw, held := a[key]
	if !held {
		return ""
	}
	switch value := raw.(type) {
	case string:
		return value
	case float64:
		return strconv.FormatFloat(value, 'f', -1, 64)
	case bool:
		return strconv.FormatBool(value)
	default:
		return ""
	}
}

func (a arguments) required(key string) (string, error) {
	found := a.text(key)
	if strings.TrimSpace(found) == "" {
		return "", fmt.Errorf("%s is required", key)
	}
	return found, nil
}

func (a arguments) number(key string, fallback int) int {
	raw, held := a[key]
	if !held {
		return fallback
	}
	switch value := raw.(type) {
	case float64:
		return int(value)
	case string:
		parsed, err := strconv.Atoi(value)
		if err != nil {
			return fallback
		}
		return parsed
	default:
		return fallback
	}
}

func (a arguments) limit(key string, fallback int) int {
	found := a.number(key, fallback)
	if found <= 0 {
		return fallback
	}
	if found > maxRows {
		return maxRows
	}
	return found
}

func (a arguments) count(key string) (int64, error) {
	raw, held := a[key]
	if !held {
		return 0, fmt.Errorf("%s is required", key)
	}
	switch value := raw.(type) {
	case float64:
		if value != math.Trunc(value) {
			return 0, fmt.Errorf("%s must be a whole number, not %v", key, value)
		}
		return int64(value), nil
	case string:
		parsed, err := strconv.ParseInt(value, 10, 64)
		if err != nil {
			return 0, fmt.Errorf("%s must be a whole number, not %q", key, value)
		}
		return parsed, nil
	default:
		return 0, fmt.Errorf("%s must be a whole number", key)
	}
}

func (a arguments) flag(key string) bool {
	raw, held := a[key]
	if !held {
		return false
	}
	switch value := raw.(type) {
	case bool:
		return value
	case string:
		return value == "true"
	default:
		return false
	}
}

func (a arguments) oneOf(key string, allowed ...string) (string, error) {
	found, err := a.required(key)
	if err != nil {
		return "", err
	}
	if slices.Contains(allowed, found) {
		return found, nil
	}
	return "", fmt.Errorf("%s must be one of %s", key, strings.Join(allowed, ", "))
}

func (a arguments) ref(catalog api.ResourceCatalog) (api.ObjectRef, error) {
	resource, err := a.required(argResource)
	if err != nil {
		return api.ObjectRef{}, err
	}
	name, err := a.required(argName)
	if err != nil {
		return api.ObjectRef{}, err
	}
	found, err := resolve(catalog, resource, a.text(argGroup))
	if err != nil {
		return api.ObjectRef{}, err
	}
	return api.ObjectRef{
		Group:     found.Group,
		Version:   found.Version,
		Resource:  found.Resource,
		Namespace: a.text(argNamespace),
		Name:      name,
	}, nil
}

func (a arguments) refIn(catalog api.ResourceCatalog) (api.ObjectRef, error) {
	ref, err := a.ref(catalog)
	if err != nil {
		return api.ObjectRef{}, err
	}
	if ref.Namespace == "" {
		return api.ObjectRef{}, fmt.Errorf("%s is required", argNamespace)
	}
	return ref, nil
}

func (a arguments) kind(catalog api.ResourceCatalog) (api.ObjectRef, error) {
	resource, err := a.required(argResource)
	if err != nil {
		return api.ObjectRef{}, err
	}
	found, err := resolve(catalog, resource, a.text(argGroup))
	if err != nil {
		return api.ObjectRef{}, err
	}
	return api.ObjectRef{
		Group:     found.Group,
		Version:   found.Version,
		Resource:  found.Resource,
		Namespace: a.text(argNamespace),
	}, nil
}

func resolve(catalog api.ResourceCatalog, resource, group string) (api.ResourceDescriptor, error) {
	matches := []api.ResourceDescriptor{}
	wanted := strings.ToLower(resource)
	for _, category := range catalog.Categories {
		for _, desc := range category.Resources {
			if !names(desc, wanted) {
				continue
			}
			if group != "" && desc.Group != group {
				continue
			}
			matches = append(matches, desc)
		}
	}
	if len(matches) == 0 {
		return api.ResourceDescriptor{}, fmt.Errorf("this cluster reports no resource type called %q", resource)
	}
	if len(matches) > 1 {
		return api.ResourceDescriptor{}, fmt.Errorf("%q is served by %s; name the group", resource, groupList(matches))
	}
	return matches[0], nil
}

func names(desc api.ResourceDescriptor, wanted string) bool {
	if strings.EqualFold(desc.Resource, wanted) {
		return true
	}
	return strings.EqualFold(desc.Kind, wanted)
}

func groupList(matches []api.ResourceDescriptor) string {
	out := make([]string, 0, len(matches))
	for _, desc := range matches {
		label := desc.Group
		if label == "" {
			label = "the core group"
		}
		out = append(out, label)
	}
	return strings.Join(out, " and ")
}