package dxcc import "strings" // The dxccByName table itself lives in dxcc_names_gen.go (generated by joining // cty.dat to the authoritative ARRL/ADIF entity list). cty.dat doesn't carry // the ADIF DXCC number, so we map its entity names → numbers here to stamp // MY_DXCC / DXCC at log time without a network round-trip. // EntityDXCC returns the ADIF DXCC entity number for the given cty.dat entity // name, or 0 when unknown (callers should then leave the field empty rather // than guess). Case-insensitive and whitespace-tolerant. func EntityDXCC(name string) int { if name == "" { return 0 } // Fast path: exact (lower-cased) match against the cty.dat names. if n := dxccByName[strings.ToLower(strings.TrimSpace(name))]; n != 0 { return n } // Fallback: canonicalise so abbreviation/spelling differences still match // (e.g. an ADIF import that wrote "Lord Howe I." instead of cty.dat's // "Lord Howe Island"). if n := dxccByCanon[canonEntity(name)]; n != 0 { return n } // Last resort: cty.dat pseudo-entities (Sicily, African Italy) report a // parent DXCC entity for the number. if c := CanonicalEntityName(name); !strings.EqualFold(c, name) { return dxccByName[strings.ToLower(strings.TrimSpace(c))] } return 0 } // dxccByCanon is dxccByName re-keyed by the canonical entity form, built once. var dxccByCanon = func() map[string]int { m := make(map[string]int, len(dxccByName)) for name, num := range dxccByName { m[canonEntity(name)] = num } return m }() // canonEntity reduces an entity name to a canonical token stream, expanding the // common abbreviations that differ between naming conventions and normalising // punctuation / "&". func canonEntity(s string) string { s = strings.ToLower(strings.TrimSpace(s)) s = strings.ReplaceAll(s, "&", " and ") fields := strings.FieldsFunc(s, func(r rune) bool { switch r { case ' ', '.', ',', '-', '\'', '(', ')', '/': return true } return false }) for i, w := range fields { switch w { case "i": fields[i] = "island" case "is": fields[i] = "islands" case "st": fields[i] = "saint" case "mt": fields[i] = "mount" case "rep": fields[i] = "republic" case "dem": fields[i] = "democratic" case "fed": fields[i] = "federal" } } return strings.Join(fields, " ") } // ctyEntityAliases maps cty.dat's non-DXCC pseudo-entities — the CQ-zone / // WAE splits AD1C lists separately — onto the ARRL DXCC entity they belong // to. cty.dat reports e.g. "Sicily" so contesters get the right zones, but // for DXCC (and the COUNTRY field) they are Italy. var ctyEntityAliases = map[string]string{ "sicily": "Italy", "african italy": "Italy", // NB: Sardinia is intentionally absent — it's a real DXCC entity. } // CanonicalEntityName normalises a cty.dat entity name to its ARRL DXCC // entity. Names that already are DXCC entities pass through unchanged. func CanonicalEntityName(name string) string { if c, ok := ctyEntityAliases[strings.ToLower(strings.TrimSpace(name))]; ok { return c } return name }