embed.go raw

   1  package kind
   2  
   3  import (
   4  	_ "embed"
   5  	"encoding/json"
   6  )
   7  
   8  //go:embed kinds.json
   9  var KindsJSON []byte
  10  
  11  // KindData represents the full structure of the kinds.json file
  12  type KindData struct {
  13  	Version    string            `json:"version"`
  14  	Source     string            `json:"source"`
  15  	Ranges     Ranges            `json:"ranges"`
  16  	Kinds      map[string]KindInfo `json:"kinds"`
  17  	Privileged []int             `json:"privileged"`
  18  	Directory  []int             `json:"directory"`
  19  	Aliases    map[string]int    `json:"aliases"`
  20  }
  21  
  22  // Ranges defines the kind number ranges for each event classification
  23  type Ranges struct {
  24  	Regular      Range `json:"regular"`
  25  	Replaceable  Range `json:"replaceable"`
  26  	Ephemeral    Range `json:"ephemeral"`
  27  	Parameterized Range `json:"parameterized"`
  28  }
  29  
  30  // Range defines a start and end for a kind range
  31  type Range struct {
  32  	Start       int    `json:"start"`
  33  	End         int    `json:"end"`
  34  	Description string `json:"description"`
  35  }
  36  
  37  // KindInfo contains metadata about a specific event kind
  38  type KindInfo struct {
  39  	Name           string  `json:"name"`
  40  	NIP            *string `json:"nip,omitempty"`
  41  	Description    string  `json:"description"`
  42  	Classification string  `json:"classification,omitempty"`
  43  	Deprecated     bool    `json:"deprecated,omitempty"`
  44  	DeprecatedBy   string  `json:"deprecatedBy,omitempty"`
  45  	Spec           string  `json:"spec,omitempty"`
  46  	RangeEnd       int     `json:"rangeEnd,omitempty"`
  47  }
  48  
  49  var kindData *KindData
  50  
  51  func init() {
  52  	kindData = &KindData{}
  53  	if err := json.Unmarshal(KindsJSON, kindData); err != nil {
  54  		panic("failed to parse kinds.json: " + err.Error())
  55  	}
  56  
  57  	// Populate the Map from JSON data for backward compatibility
  58  	MapMx.Lock()
  59  	defer MapMx.Unlock()
  60  	for kStr, info := range kindData.Kinds {
  61  		var k int
  62  		if err := json.Unmarshal([]byte(kStr), &k); err == nil {
  63  			Map[uint16(k)] = info.Name
  64  		}
  65  	}
  66  }
  67  
  68  // GetKindData returns the parsed kind data from the embedded JSON
  69  func GetKindData() *KindData {
  70  	return kindData
  71  }
  72  
  73  // GetKindInfo returns the KindInfo for a specific kind number
  74  func GetKindInfo(k uint16) (KindInfo, bool) {
  75  	kStr := json.Number(string(rune('0' + k))).String()
  76  	// Manual conversion since json.Number doesn't work like that
  77  	kStr = itoa(int(k))
  78  	info, ok := kindData.Kinds[kStr]
  79  	return info, ok
  80  }
  81  
  82  func itoa(n int) string {
  83  	if n == 0 {
  84  		return "0"
  85  	}
  86  
  87  	var digits []byte
  88  	negative := n < 0
  89  	if negative {
  90  		n = -n
  91  	}
  92  
  93  	for n > 0 {
  94  		digits = append([]byte{byte('0' + n%10)}, digits...)
  95  		n /= 10
  96  	}
  97  
  98  	if negative {
  99  		digits = append([]byte{'-'}, digits...)
 100  	}
 101  
 102  	return string(digits)
 103  }
 104  
 105  // GetDescription returns the description for a kind number
 106  func GetDescription(k uint16) string {
 107  	if info, ok := GetKindInfo(k); ok {
 108  		return info.Description
 109  	}
 110  	return ""
 111  }
 112  
 113  // GetNIP returns the NIP reference for a kind number
 114  func GetNIP(k uint16) string {
 115  	if info, ok := GetKindInfo(k); ok && info.NIP != nil {
 116  		return *info.NIP
 117  	}
 118  	return ""
 119  }
 120  
 121  // IsDeprecated returns whether a kind is deprecated
 122  func IsDeprecated(k uint16) bool {
 123  	if info, ok := GetKindInfo(k); ok {
 124  		return info.Deprecated
 125  	}
 126  	return false
 127  }
 128  
 129  // GetClassification returns the classification (regular, replaceable, ephemeral, parameterized) for a kind
 130  func GetClassification(k uint16) string {
 131  	// First check explicit classification in JSON
 132  	if info, ok := GetKindInfo(k); ok && info.Classification != "" {
 133  		return info.Classification
 134  	}
 135  
 136  	// Fall back to range-based classification
 137  	if k >= uint16(kindData.Ranges.Parameterized.Start) && k <= uint16(kindData.Ranges.Parameterized.End) {
 138  		return "parameterized"
 139  	}
 140  	if k >= uint16(kindData.Ranges.Ephemeral.Start) && k < uint16(kindData.Ranges.Ephemeral.End) {
 141  		return "ephemeral"
 142  	}
 143  	if k >= uint16(kindData.Ranges.Replaceable.Start) && k < uint16(kindData.Ranges.Replaceable.End) {
 144  		return "replaceable"
 145  	}
 146  	if k >= uint16(kindData.Ranges.Regular.Start) && k <= uint16(kindData.Ranges.Regular.End) {
 147  		return "regular"
 148  	}
 149  
 150  	// Core kinds 0 and 3 are replaceable
 151  	if k == 0 || k == 3 {
 152  		return "replaceable"
 153  	}
 154  
 155  	return "regular"
 156  }
 157