47GPFVLW7RWBBHHUZYMEEYWG3KBJBWELR7RDKMJRWMNRWYJUBR7QC NKQAT3RE4IBIWXVMI5LJUINDPHTANNMORZ5N2JFA4AN6UUB72KGAC X3K56A54LNNXODOH6MK22NTSEUQ54BUEZ3EL6ANKXYNL4RROL73QC IFVRAERTCCDICNTYTG3TX2WASB6RXQQEJWWXQMQZJSQDQ3HLE5OQC DORZF5HSV672ZP5HUDYB3J6TBH5O2LMXJE4HPSE7H5SOGZQBDCXQC OGLLBQQYE5KICDMI6EX7ZI4TZT5RB7UFHH7O2DUOZ44QQXVL5YAAC 7NS27QXZMVTZBK4VPMYL5IKGSTTAWR6NDG5SOVITNX44VNIRZPMAC OCRETPZZPDCUSOPYRH5MVRATJ37TRFGVSIMOI4IV755HFXXOVHEAC 5LMYPB2QHNVDLYCRWLOMCPY35ZKHHPYVW5XHASE66L6PJZSOCXYQC VZGXBNYYO3E7EPFQ4GOLNVMRXXTQDDQZUU2BZ6JHNBDY4B2QLDAAC package utilsimport ("testing")func TestValidateShortID(t *testing.T) {tests := []struct {name stringid stringfieldName stringwantErr bool}{{"valid 12-char ID", "abc123XYZ789", "test_id", false},{"valid with underscore", "abc_123_XYZ_", "test_id", false},{"valid with dash", "abc-123-XYZ-", "test_id", false},{"empty string", "", "test_id", true},{"too short", "abc123", "test_id", true},{"too long", "abc123XYZ789toolong", "test_id", true},{"invalid chars", "abc@123#XYZ$", "test_id", true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateShortID(tt.id, tt.fieldName)if (err != nil) != tt.wantErr {t.Errorf("ValidateShortID() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateLongID(t *testing.T) {tests := []struct {name stringid stringfieldName stringwantErr bool}{{"valid 21-char ID", "abc123XYZ789abc123XYZ", "test_id", false}, // exactly 21 chars{"valid with underscore", "abc_123_XYZ_789_abc_X", "test_id", false}, // exactly 21 chars{"empty string", "", "test_id", true},{"too short", "abc123XYZ789", "test_id", true}, // 12 chars{"too long", "abc123XYZ789abc123XYZ789ex", "test_id", true}, // 24 chars{"invalid chars", "abc@123#XYZ$789%abc^XY", "test_id", true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateLongID(tt.id, tt.fieldName)if (err != nil) != tt.wantErr {t.Errorf("ValidateLongID() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateStringLength(t *testing.T) {tests := []struct {name stringvalue stringfield stringmaxLen intwantErr bool}{{"within limit", "hello", "test", 10, false},{"at limit", "1234567890", "test", 10, false},{"empty string", "", "test", 10, false},{"over limit", "12345678901", "test", 10, true},{"zero max", "a", "test", 0, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateStringLength(tt.value, tt.field, tt.maxLen)if (err != nil) != tt.wantErr {t.Errorf("ValidateStringLength() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateRange(t *testing.T) {t.Run("int range", func(t *testing.T) {tests := []struct {name stringvalue intmin intmax intwantErr bool}{{"within range", 50, 0, 100, false},{"at min", 0, 0, 100, false},{"at max", 100, 0, 100, false},{"below min", -1, 0, 100, true},{"above max", 101, 0, 100, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateRange(tt.value, "test", tt.min, tt.max)if (err != nil) != tt.wantErr {t.Errorf("ValidateRange() error = %v, wantErr %v", err, tt.wantErr)}})}})t.Run("float64 range", func(t *testing.T) {tests := []struct {name stringvalue float64min float64max float64wantErr bool}{{"within range", 45.5, -90.0, 90.0, false},{"at min", -90.0, -90.0, 90.0, false},{"at max", 90.0, -90.0, 90.0, false},{"below min", -90.1, -90.0, 90.0, true},{"above max", 90.1, -90.0, 90.0, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateRange(tt.value, "test", tt.min, tt.max)if (err != nil) != tt.wantErr {t.Errorf("ValidateRange() error = %v, wantErr %v", err, tt.wantErr)}})}})}func TestValidatePositive(t *testing.T) {tests := []struct {name stringvalue intwantErr bool}{{"positive", 1, false},{"large positive", 1000000, false},{"zero", 0, true},{"negative", -1, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidatePositive(tt.value, "test")if (err != nil) != tt.wantErr {t.Errorf("ValidatePositive() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateSampleRate(t *testing.T) {tests := []struct {name stringrate intwantErr bool}{{"valid low", 1000, false},{"valid typical", 48000, false},{"valid high", 250000, false},{"valid max", 500000, false},{"too low", 999, true},{"too high", 500001, true},{"zero", 0, true},{"negative", -1000, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateSampleRate(tt.rate)if (err != nil) != tt.wantErr {t.Errorf("ValidateSampleRate() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateHash(t *testing.T) {tests := []struct {name stringhash stringwantErr bool}{{"valid hash", "0123456789abcdef", false},{"valid all letters", "abcdefabcdefabcd", false},{"valid all numbers", "1234567890123456", false},{"too short", "0123456789abcde", true},{"too long", "0123456789abcdef0", true},{"invalid chars", "ghijklmnopqrstuv", true},{"uppercase", "ABCDEF1234567890", true},{"mixed case", "aBcDeF1234567890", true},{"empty", "", true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateHash(tt.hash, "test_hash")if (err != nil) != tt.wantErr {t.Errorf("ValidateHash() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateFrequencyRange(t *testing.T) {low := func(v float64) *float64 { return &v }high := func(v float64) *float64 { return &v }tests := []struct {name stringfreqLow *float64freqHigh *float64wantErr bool}{{"both nil", nil, nil, false},{"valid range", low(100.0), high(1000.0), false},{"valid low only", low(100.0), nil, false},{"valid high only", nil, high(1000.0), false},{"at bounds", low(0.0), high(299999.0), false},{"low negative", low(-1.0), high(1000.0), true},{"high too high", low(100.0), high(300000.0), true},{"low equals high", low(500.0), high(500.0), true},{"low greater than high", low(1000.0), high(100.0), true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateFrequencyRange(tt.freqLow, tt.freqHigh)if (err != nil) != tt.wantErr {t.Errorf("ValidateFrequencyRange() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateCertainty(t *testing.T) {val := func(v float64) *float64 { return &v }tests := []struct {name stringcertainty *float64wantErr bool}{{"nil", nil, false},{"zero", val(0.0), false},{"mid range", val(50.0), false},{"100", val(100.0), false},{"negative", val(-0.1), true},{"over 100", val(100.1), true},{"much over", val(200.0), true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateCertainty(tt.certainty)if (err != nil) != tt.wantErr {t.Errorf("ValidateCertainty() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateTimezone(t *testing.T) {tests := []struct {name stringtz stringwantErr bool}{{"valid Auckland", "Pacific/Auckland", false},{"valid UTC", "UTC", false},{"valid America/New_York", "America/New_York", false},{"valid Europe/London", "Europe/London", false},{"invalid", "Invalid/Timezone", true},{"garbage", "not-a-timezone", true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateTimezone(tt.tz)if (err != nil) != tt.wantErr {t.Errorf("ValidateTimezone() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateNonNegative(t *testing.T) {tests := []struct {name stringvalue intwantErr bool}{{"positive", 1, false},{"zero", 0, false},{"negative", -1, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateNonNegative(tt.value, "test")if (err != nil) != tt.wantErr {t.Errorf("ValidateNonNegative() error = %v, wantErr %v", err, tt.wantErr)}})}}
package utilsimport ("database/sql""fmt""regexp""time")// ID length constants matching nanoid generationconst (ShortIDLen = 12 // dataset, location, cluster, pattern, species, filter, call_typeLongIDLen = 21 // file, selection, label, label_subtype)// Sample rate reasonable bounds for audio recordingconst (MinSampleRate = 1000 // 1 kHz - below this is unlikely to be real audioMaxSampleRate = 500000 // 500 kHz - well above bat detectors (~250kHz))// Max string lengths from schemaconst (MaxNameLen = 140 // location.name, cluster.nameMaxDatasetNameLen = 255 // dataset.nameMaxDescriptionLen = 255 // all description fieldsMaxPathLen = 255 // cluster.pathMaxFileNameLen = 255 // file.file_nameMaxTimezoneLen = 40 // location.timezone_idHashLen = 16 // xxh64_hash)// ID format regex - alphanumeric characters (nanoid uses A-Za-z0-9_)var shortIDRegex = regexp.MustCompile(`^[A-Za-z0-9_-]{12}$`)var longIDRegex = regexp.MustCompile(`^[A-Za-z0-9_-]{21}$`)var hashRegex = regexp.MustCompile(`^[0-9a-f]{16}$`)// ValidateShortID validates 12-character nanoid formatfunc ValidateShortID(id, fieldName string) error {if id == "" {return fmt.Errorf("%s cannot be empty", fieldName)}if len(id) != ShortIDLen {return fmt.Errorf("%s must be exactly %d characters (got %d)", fieldName, ShortIDLen, len(id))}if !shortIDRegex.MatchString(id) {return fmt.Errorf("%s has invalid format (expected alphanumeric nanoid)", fieldName)}return nil}// ValidateLongID validates 21-character nanoid formatfunc ValidateLongID(id, fieldName string) error {if id == "" {return fmt.Errorf("%s cannot be empty", fieldName)}if len(id) != LongIDLen {return fmt.Errorf("%s must be exactly %d characters (got %d)", fieldName, LongIDLen, len(id))}if !longIDRegex.MatchString(id) {return fmt.Errorf("%s has invalid format (expected alphanumeric nanoid)", fieldName)}return nil}// ValidateOptionalShortID validates short ID if provided (non-empty)func ValidateOptionalShortID(id *string, fieldName string) error {if id == nil || *id == "" {return nil}return ValidateShortID(*id, fieldName)}// ValidateOptionalLongID validates long ID if provided (non-empty)func ValidateOptionalLongID(id *string, fieldName string) error {if id == nil || *id == "" {return nil}return ValidateLongID(*id, fieldName)}// ValidateStringLength validates string length constraintfunc ValidateStringLength(value, fieldName string, maxLen int) error {if len(value) > maxLen {return fmt.Errorf("%s must be %d characters or less (got %d)", fieldName, maxLen, len(value))}return nil}// ValidateOptionalStringLength validates string length if providedfunc ValidateOptionalStringLength(value *string, fieldName string, maxLen int) error {if value == nil || *value == "" {return nil}return ValidateStringLength(*value, fieldName, maxLen)}// ValidateRange validates numeric range constraint (inclusive)func ValidateRange[T int | float64](value T, fieldName string, min, max T) error {if value < min || value > max {return fmt.Errorf("%s must be between %v and %v (got %v)", fieldName, min, max, value)}return nil}// ValidatePositive validates positive number (> 0)func ValidatePositive[T int | float64](value T, fieldName string) error {if value <= 0 {return fmt.Errorf("%s must be positive (got %v)", fieldName, value)}return nil}// ValidateNonNegative validates non-negative number (>= 0)func ValidateNonNegative[T int | float64](value T, fieldName string) error {if value < 0 {return fmt.Errorf("%s must be non-negative (got %v)", fieldName, value)}return nil}// ValidateSampleRate validates audio sample rate is in reasonable rangefunc ValidateSampleRate(rate int) error {return ValidateRange(rate, "sample_rate", MinSampleRate, MaxSampleRate)}// ValidateTimezone validates IANA timezone IDfunc ValidateTimezone(tzID string) error {if _, err := time.LoadLocation(tzID); err != nil {return fmt.Errorf("invalid timezone_id '%s': %w", tzID, err)}return nil}// ValidateHash validates XXH64 hash format (16 hex characters)func ValidateHash(hash, fieldName string) error {if len(hash) != HashLen {return fmt.Errorf("%s must be exactly %d characters (got %d)", fieldName, HashLen, len(hash))}if !hashRegex.MatchString(hash) {return fmt.Errorf("%s has invalid format (expected 16 hex characters)", fieldName)}return nil}// ValidateFrequencyRange validates frequency bounds for selectionsfunc ValidateFrequencyRange(freqLow, freqHigh *float64) error {if freqLow == nil && freqHigh == nil {return nil}// Schema limit is 300000 Hzconst maxFreq = 300000.0if freqLow != nil {if *freqLow < 0 {return fmt.Errorf("freq_low must be non-negative (got %v)", *freqLow)}if *freqLow >= maxFreq {return fmt.Errorf("freq_low must be less than %v Hz (got %v)", maxFreq, *freqLow)}}if freqHigh != nil {if *freqHigh < 0 {return fmt.Errorf("freq_high must be non-negative (got %v)", *freqHigh)}if *freqHigh >= maxFreq {return fmt.Errorf("freq_high must be less than %v Hz (got %v)", maxFreq, *freqHigh)}}// If both provided, low must be less than highif freqLow != nil && freqHigh != nil && *freqLow >= *freqHigh {return fmt.Errorf("freq_low (%v) must be less than freq_high (%v)", *freqLow, *freqHigh)}return nil}// ValidateCertainty validates certainty percentage (0-100)func ValidateCertainty(certainty *float64) error {if certainty == nil {return nil}if *certainty < 0 || *certainty > 100 {return fmt.Errorf("certainty must be between 0 and 100 (got %v)", *certainty)}return nil}// EntityExists checks if an entity exists in the databasefunc EntityExists(db *sql.DB, table, id string) (bool, error) {var exists boolquery := fmt.Sprintf("SELECT EXISTS(SELECT 1 FROM %s WHERE id = ?)", table)err := db.QueryRow(query, id).Scan(&exists)return exists, err}// EntityExistsAndActive checks if an entity exists and is activefunc EntityExistsAndActive(db *sql.DB, table, id string) (bool, error) {var exists boolquery := fmt.Sprintf("SELECT EXISTS(SELECT 1 FROM %s WHERE id = ? AND active = true)", table)err := db.QueryRow(query, id).Scan(&exists)return exists, err}// GetEntityActiveStatus returns whether an entity is active (and exists)// Returns: (exists, active, error)func GetEntityActiveStatus(db *sql.DB, table, id string) (bool, bool, error) {var exists boolvar active boolquery := fmt.Sprintf("SELECT EXISTS(SELECT 1 FROM %s WHERE id = ?), COALESCE((SELECT active FROM %s WHERE id = ?), false)", table, table)err := db.QueryRow(query, id, id).Scan(&exists, &active)return exists, active, err}
package toolsimport ("testing")func TestValidateShortID(t *testing.T) {tests := []struct {name stringid stringfieldName stringwantErr bool}{{"valid 12-char ID", "abc123XYZ789", "test_id", false},{"valid with underscore", "abc_123_XYZ_", "test_id", false},{"valid with dash", "abc-123-XYZ-", "test_id", false},{"empty string", "", "test_id", true},{"too short", "abc123", "test_id", true},{"too long", "abc123XYZ789toolong", "test_id", true},{"invalid chars", "abc@123#XYZ$", "test_id", true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateShortID(tt.id, tt.fieldName)if (err != nil) != tt.wantErr {t.Errorf("ValidateShortID() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateLongID(t *testing.T) {tests := []struct {name stringid stringfieldName stringwantErr bool}{{"valid 21-char ID", "abc123XYZ789abc123XYZ", "test_id", false}, // exactly 21 chars{"valid with underscore", "abc_123_XYZ_789_abc_X", "test_id", false}, // exactly 21 chars{"empty string", "", "test_id", true},{"too short", "abc123XYZ789", "test_id", true}, // 12 chars{"too long", "abc123XYZ789abc123XYZ789ex", "test_id", true}, // 24 chars{"invalid chars", "abc@123#XYZ$789%abc^XY", "test_id", true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateLongID(tt.id, tt.fieldName)if (err != nil) != tt.wantErr {t.Errorf("ValidateLongID() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateStringLength(t *testing.T) {tests := []struct {name stringvalue stringfield stringmaxLen intwantErr bool}{{"within limit", "hello", "test", 10, false},{"at limit", "1234567890", "test", 10, false},{"empty string", "", "test", 10, false},{"over limit", "12345678901", "test", 10, true},{"zero max", "a", "test", 0, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateStringLength(tt.value, tt.field, tt.maxLen)if (err != nil) != tt.wantErr {t.Errorf("ValidateStringLength() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateRange(t *testing.T) {t.Run("int range", func(t *testing.T) {tests := []struct {name stringvalue intmin intmax intwantErr bool}{{"within range", 50, 0, 100, false},{"at min", 0, 0, 100, false},{"at max", 100, 0, 100, false},{"below min", -1, 0, 100, true},{"above max", 101, 0, 100, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateRange(tt.value, "test", tt.min, tt.max)if (err != nil) != tt.wantErr {t.Errorf("ValidateRange() error = %v, wantErr %v", err, tt.wantErr)}})}})t.Run("float64 range", func(t *testing.T) {tests := []struct {name stringvalue float64min float64max float64wantErr bool}{{"within range", 45.5, -90.0, 90.0, false},{"at min", -90.0, -90.0, 90.0, false},{"at max", 90.0, -90.0, 90.0, false},{"below min", -90.1, -90.0, 90.0, true},{"above max", 90.1, -90.0, 90.0, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateRange(tt.value, "test", tt.min, tt.max)if (err != nil) != tt.wantErr {t.Errorf("ValidateRange() error = %v, wantErr %v", err, tt.wantErr)}})}})}func TestValidatePositive(t *testing.T) {tests := []struct {name stringvalue intwantErr bool}{{"positive", 1, false},{"large positive", 1000000, false},{"zero", 0, true},{"negative", -1, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidatePositive(tt.value, "test")if (err != nil) != tt.wantErr {t.Errorf("ValidatePositive() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateSampleRate(t *testing.T) {tests := []struct {name stringrate intwantErr bool}{{"valid low", 1000, false},{"valid typical", 48000, false},{"valid high", 250000, false},{"valid max", 500000, false},{"too low", 999, true},{"too high", 500001, true},{"zero", 0, true},{"negative", -1000, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateSampleRate(tt.rate)if (err != nil) != tt.wantErr {t.Errorf("ValidateSampleRate() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateHash(t *testing.T) {tests := []struct {name stringhash stringwantErr bool}{{"valid hash", "0123456789abcdef", false},{"valid all letters", "abcdefabcdefabcd", false},{"valid all numbers", "1234567890123456", false},{"too short", "0123456789abcde", true},{"too long", "0123456789abcdef0", true},{"invalid chars", "ghijklmnopqrstuv", true},{"uppercase", "ABCDEF1234567890", true},{"mixed case", "aBcDeF1234567890", true},{"empty", "", true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateHash(tt.hash, "test_hash")if (err != nil) != tt.wantErr {t.Errorf("ValidateHash() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateFrequencyRange(t *testing.T) {low := func(v float64) *float64 { return &v }high := func(v float64) *float64 { return &v }tests := []struct {name stringfreqLow *float64freqHigh *float64wantErr bool}{{"both nil", nil, nil, false},{"valid range", low(100.0), high(1000.0), false},{"valid low only", low(100.0), nil, false},{"valid high only", nil, high(1000.0), false},{"at bounds", low(0.0), high(299999.0), false},{"low negative", low(-1.0), high(1000.0), true},{"high too high", low(100.0), high(300000.0), true},{"low equals high", low(500.0), high(500.0), true},{"low greater than high", low(1000.0), high(100.0), true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateFrequencyRange(tt.freqLow, tt.freqHigh)if (err != nil) != tt.wantErr {t.Errorf("ValidateFrequencyRange() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateCertainty(t *testing.T) {val := func(v float64) *float64 { return &v }tests := []struct {name stringcertainty *float64wantErr bool}{{"nil", nil, false},{"zero", val(0.0), false},{"mid range", val(50.0), false},{"100", val(100.0), false},{"negative", val(-0.1), true},{"over 100", val(100.1), true},{"much over", val(200.0), true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateCertainty(tt.certainty)if (err != nil) != tt.wantErr {t.Errorf("ValidateCertainty() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateTimezone(t *testing.T) {tests := []struct {name stringtz stringwantErr bool}{{"valid Auckland", "Pacific/Auckland", false},{"valid UTC", "UTC", false},{"valid America/New_York", "America/New_York", false},{"valid Europe/London", "Europe/London", false},{"invalid", "Invalid/Timezone", true},{"garbage", "not-a-timezone", true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateTimezone(tt.tz)if (err != nil) != tt.wantErr {t.Errorf("ValidateTimezone() error = %v, wantErr %v", err, tt.wantErr)}})}}func TestValidateNonNegative(t *testing.T) {tests := []struct {name stringvalue intwantErr bool}{{"positive", 1, false},{"zero", 0, false},{"negative", -1, true},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {err := ValidateNonNegative(tt.value, "test")if (err != nil) != tt.wantErr {t.Errorf("ValidateNonNegative() error = %v, wantErr %v", err, tt.wantErr)}})}}
package toolsimport ("database/sql""fmt""regexp""time")// ID length constants matching nanoid generationconst (ShortIDLen = 12 // dataset, location, cluster, pattern, species, filter, call_typeLongIDLen = 21 // file, selection, label, label_subtype)// Sample rate reasonable bounds for audio recordingconst (MinSampleRate = 1000 // 1 kHz - below this is unlikely to be real audioMaxSampleRate = 500000 // 500 kHz - well above bat detectors (~250kHz))// Max string lengths from schemaconst (MaxNameLen = 140 // location.name, cluster.nameMaxDatasetNameLen = 255 // dataset.nameMaxDescriptionLen = 255 // all description fieldsMaxPathLen = 255 // cluster.pathMaxFileNameLen = 255 // file.file_nameMaxTimezoneLen = 40 // location.timezone_idHashLen = 16 // xxh64_hash)// ID format regex - alphanumeric characters (nanoid uses A-Za-z0-9_)var shortIDRegex = regexp.MustCompile(`^[A-Za-z0-9_-]{12}$`)var longIDRegex = regexp.MustCompile(`^[A-Za-z0-9_-]{21}$`)var hashRegex = regexp.MustCompile(`^[0-9a-f]{16}$`)// ValidateShortID validates 12-character nanoid formatfunc ValidateShortID(id, fieldName string) error {if id == "" {return fmt.Errorf("%s cannot be empty", fieldName)}if len(id) != ShortIDLen {return fmt.Errorf("%s must be exactly %d characters (got %d)", fieldName, ShortIDLen, len(id))}if !shortIDRegex.MatchString(id) {return fmt.Errorf("%s has invalid format (expected alphanumeric nanoid)", fieldName)}return nil}// ValidateLongID validates 21-character nanoid formatfunc ValidateLongID(id, fieldName string) error {if id == "" {return fmt.Errorf("%s cannot be empty", fieldName)}if len(id) != LongIDLen {return fmt.Errorf("%s must be exactly %d characters (got %d)", fieldName, LongIDLen, len(id))}if !longIDRegex.MatchString(id) {return fmt.Errorf("%s has invalid format (expected alphanumeric nanoid)", fieldName)}return nil}// ValidateOptionalShortID validates short ID if provided (non-empty)func ValidateOptionalShortID(id *string, fieldName string) error {if id == nil || *id == "" {return nil}return ValidateShortID(*id, fieldName)}// ValidateOptionalLongID validates long ID if provided (non-empty)func ValidateOptionalLongID(id *string, fieldName string) error {if id == nil || *id == "" {return nil}return ValidateLongID(*id, fieldName)}// ValidateStringLength validates string length constraintfunc ValidateStringLength(value, fieldName string, maxLen int) error {if len(value) > maxLen {return fmt.Errorf("%s must be %d characters or less (got %d)", fieldName, maxLen, len(value))}return nil}// ValidateOptionalStringLength validates string length if providedfunc ValidateOptionalStringLength(value *string, fieldName string, maxLen int) error {if value == nil || *value == "" {return nil}return ValidateStringLength(*value, fieldName, maxLen)}// ValidateRange validates numeric range constraint (inclusive)func ValidateRange[T int | float64](value T, fieldName string, min, max T) error {if value < min || value > max {return fmt.Errorf("%s must be between %v and %v (got %v)", fieldName, min, max, value)}return nil}// ValidatePositive validates positive number (> 0)func ValidatePositive[T int | float64](value T, fieldName string) error {if value <= 0 {return fmt.Errorf("%s must be positive (got %v)", fieldName, value)}return nil}// ValidateNonNegative validates non-negative number (>= 0)func ValidateNonNegative[T int | float64](value T, fieldName string) error {if value < 0 {return fmt.Errorf("%s must be non-negative (got %v)", fieldName, value)}return nil}// ValidateSampleRate validates audio sample rate is in reasonable rangefunc ValidateSampleRate(rate int) error {return ValidateRange(rate, "sample_rate", MinSampleRate, MaxSampleRate)}// ValidateTimezone validates IANA timezone IDfunc ValidateTimezone(tzID string) error {if _, err := time.LoadLocation(tzID); err != nil {return fmt.Errorf("invalid timezone_id '%s': %w", tzID, err)}return nil}// ValidateHash validates XXH64 hash format (16 hex characters)func ValidateHash(hash, fieldName string) error {if len(hash) != HashLen {return fmt.Errorf("%s must be exactly %d characters (got %d)", fieldName, HashLen, len(hash))}if !hashRegex.MatchString(hash) {return fmt.Errorf("%s has invalid format (expected 16 hex characters)", fieldName)}return nil}// ValidateFrequencyRange validates frequency bounds for selectionsfunc ValidateFrequencyRange(freqLow, freqHigh *float64) error {if freqLow == nil && freqHigh == nil {return nil}// Schema limit is 300000 Hzconst maxFreq = 300000.0if freqLow != nil {if *freqLow < 0 {return fmt.Errorf("freq_low must be non-negative (got %v)", *freqLow)}if *freqLow >= maxFreq {return fmt.Errorf("freq_low must be less than %v Hz (got %v)", maxFreq, *freqLow)}}if freqHigh != nil {if *freqHigh < 0 {return fmt.Errorf("freq_high must be non-negative (got %v)", *freqHigh)}if *freqHigh >= maxFreq {return fmt.Errorf("freq_high must be less than %v Hz (got %v)", maxFreq, *freqHigh)}}// If both provided, low must be less than highif freqLow != nil && freqHigh != nil && *freqLow >= *freqHigh {return fmt.Errorf("freq_low (%v) must be less than freq_high (%v)", *freqLow, *freqHigh)}return nil}// ValidateCertainty validates certainty percentage (0-100)func ValidateCertainty(certainty *float64) error {if certainty == nil {return nil}if *certainty < 0 || *certainty > 100 {return fmt.Errorf("certainty must be between 0 and 100 (got %v)", *certainty)}return nil}// EntityExists checks if an entity exists in the databasefunc EntityExists(db *sql.DB, table, id string) (bool, error) {var exists boolquery := fmt.Sprintf("SELECT EXISTS(SELECT 1 FROM %s WHERE id = ?)", table)err := db.QueryRow(query, id).Scan(&exists)return exists, err}// EntityExistsAndActive checks if an entity exists and is activefunc EntityExistsAndActive(db *sql.DB, table, id string) (bool, error) {var exists boolquery := fmt.Sprintf("SELECT EXISTS(SELECT 1 FROM %s WHERE id = ? AND active = true)", table)err := db.QueryRow(query, id).Scan(&exists)return exists, err}// GetEntityActiveStatus returns whether an entity is active (and exists)// Returns: (exists, active, error)func GetEntityActiveStatus(db *sql.DB, table, id string) (bool, bool, error) {var exists boolvar active boolquery := fmt.Sprintf("SELECT EXISTS(SELECT 1 FROM %s WHERE id = ?), COALESCE((SELECT active FROM %s WHERE id = ?), false)", table, table)err := db.QueryRow(query, id, id).Scan(&exists, &active)return exists, active, err}
- `import bulk` - Bulk import from CSV file- `import file` - Import a single WAV file- `import folder` - Import all WAV files from a folder- `import selections` - Import ML selections from folder structure
- `import bulk` - Bulk import from CSV file (structured dataset)- `import file` - Import a single WAV file (structured dataset)- `import folder` - Import all WAV files from a folder (structured dataset)- `import selections` - Import ML selections from folder structure (structured dataset)
## Package Organization Policy### tools/ vs utils/ - When to Use Which**`utils/`** - Pure utility functions:- No MCP tool types (no `*Input`/`*Output` structs)- Pure Go functions (input → output, minimal side effects)- Can have database dependency for utility queries (e.g., `EntityExists()`)- Reusable across tools, CLI, and other packages- Examples: `ValidateXxx()`, `ParseFilename()`, `ComputeHash()`, `EntityExists()`**`tools/`** - MCP tool implementations:- MCP-specific types (`DatasetInput`, `ClusterOutput`, etc.)- Business logic layer between CLI/MCP and database- Orchestrates validation, database operations, and response formatting- One file per MCP tool (e.g., `dataset.go`, `cluster.go`)- Imports `utils/` for validation and helpers**Decision checklist:**1. Does it define MCP tool types? → `tools/`2. Is it a reusable helper function? → `utils/`3. Does multiple tools/CLI need it? → `utils/`4. Is it specific to one MCP tool's logic? → `tools/`**Examples:**| Function | Package | Reason ||----------|---------|--------|| `ValidateShortID()` | `utils/` | Pure validation, reusable || `EntityExists()` | `utils/` | Database helper, reusable || `CreateOrUpdateDataset()` | `tools/` | MCP tool implementation || `ImportCluster()` | `utils/` | Reusable import logic || `DatasetInput` struct | `tools/` | MCP-specific type |**Key insight:** The package boundary is about **reuse and MCP coupling**, not database dependency. `utils/` can use the database for utility queries, but it should never define MCP tool types.