idna2008-1.0.0.0: internal/Text/IDNA2008/Internal/UTS46.hs
-- |
-- Module : Text.IDNA2008.Internal.UTS46
-- Description : Hand-curated allow-list of UTS #46 mappings adopted
-- by the @MAPUTS46@ option, beyond strict IDNA2008.
-- Copyright : (c) Viktor Dukhovni, 2026
-- License : BSD-3-Clause
--
-- Maintainer : ietf-dane@dukhovni.org
-- Stability : unstable
--
-- A single lookup, 'uts46Lookup', returning the multi-codepoint
-- target of each source codepoint in the @MAPUTS46@ allow-list, or
-- 'Nothing' for codepoints outside the allow-list. Used by the
-- pre-validation MAPUTS46 pass in "Text.IDNA2008.Internal.Parse"
-- when the @MAPUTS46@ option is set. The full list of in\\/out
-- decisions and rationale lives in the code-comment block in the
-- source of this module, immediately above 'uts46Lookup'.
{-# LANGUAGE LambdaCase #-}
module Text.IDNA2008.Internal.UTS46
( uts46Lookup
) where
-- ---------------------------------------------------------------------------
-- Rationale -- scope of the MAPUTS46 allow-list
-- ---------------------------------------------------------------------------
--
-- Strict IDNA2008 has no notion of compatibility folding beyond the four
-- RFC 5895 input mappings (MAPDOTS, MAPCASE, MAPNFC, MAPWIDTH). UTS #46
-- offers a much larger table of mappings (~6400 entries in Unicode
-- 17.0.0). We adopt only the small subset that passes a joint test:
--
-- 1. SOURCE. An IME or normal text-entry path might inadvertently
-- produce this codepoint while a user attempts to type a domain-
-- name component. Codepoints requiring a Unicode picker, math
-- editor, or typesetting tool are NOT in scope.
--
-- 2. TARGET. The mapping result is plausible content for a real
-- domain label — a name, word, or letter sequence in a script
-- that registrars actually accept — rather than an editorial
-- expansion of a symbol.
--
-- Both halves must pass. Failure of either disqualifies the entry.
--
-- IN -- Japanese era / corporation IME shortcuts (6 entries)
-- ----------------------------------------------------------
-- Modern Japanese IMEs (Mozc, Microsoft IME, Google IME) propose these
-- single-codepoint squared forms as one-keystroke alternatives to typing
-- out the spelling. The expanded targets are exactly what an attentive
-- user would have chosen.
--
-- U+32FF (SQUARE ERA NAME REIWA) -> U+4EE4 U+548C (Reiwa)
-- U+337B (SQUARE ERA NAME HEISEI) -> U+5E73 U+6210 (Heisei)
-- U+337C (SQUARE ERA NAME SYOUWA) -> U+662D U+548C (Showa)
-- U+337D (SQUARE ERA NAME TAISYOU) -> U+5927 U+6B63 (Taisho)
-- U+337E (SQUARE ERA NAME MEIZI) -> U+660E U+6CBB (Meiji)
--
-- IN -- Circled CJK ideographs at U+3244..U+3247 and U+3280..U+32B0
-- EXCLUDING U+3297 and U+3299 (51 entries)
-- ----------------------------------------------------------------
-- Each source codepoint maps to a single CJK ideograph (e.g. U+3280
-- CIRCLED IDEOGRAPH ONE -> U+4E00, U+329F CIRCLED IDEOGRAPH ATTENTION
-- -> U+6CE8). These are pre-emoji codepoints from the CJK
-- Compatibility block that some Japanese/Chinese IMEs propose as a
-- stylistic option when marking lists or notes; the target ideograph
-- is everyday content. Two codepoints in this range (U+3297 and
-- U+3299) are deliberately omitted on the same grounds as the
-- squared CJK ideograph emoji; see the OUT list below.
--
-- OUT -- Squared CJK ideograph emoji (U+1F210..U+1F23B, U+1F250..U+1F251)
-- ----------------------------------------------------------------------
-- The UTS #46 mapping table folds these to single CJK ideographs
-- (e.g. U+1F238 SQUARED CJK UNIFIED IDEOGRAPH-7533 maps to U+7533).
-- We do NOT adopt these mappings.
--
-- Empirical (May 2026, dataset of 6300 registered emoji-bearing IDNs):
-- the .ws registry has registered xn--q97h.ws (U+1F238 as a label
-- under .ws) as a parking-lot domain. Safari, applying UTS #46
-- mapping, silently routes lookups of that label to xn--uny.ws
-- (U+7533 as a label under .ws), which is a SEPARATELY registered
-- domain belonging to a different operator (Quarken URL Shortener).
-- The two namespaces are distinct, the user's intended destination
-- and actual destination differ, and the user has no signal that the
-- rewrite occurred. Future re-registrations could turn the kanji-
-- form target into a phishing site without the emoji-form
-- registrant's knowledge or consent.
--
-- libidn2 in non-transitional mode rejects these codepoints outright
-- ("character forbidden in non-transitional mode"); the mainstream
-- tooling landscape is split, with browsers folding and BIND/dig
-- refusing. Our refusing-to-fold position is conservative but
-- defensible. See the EMOJIOK pattern in
-- "Text.IDNA2008.Internal.Flags" for the parallel decision on the
-- admit-as-is side: these codepoints are excluded from EMOJIOK too,
-- so under no combination of flags will this library route a
-- U+1F238 label to a U+7533 label.
--
-- OUT -- U+3297 CIRCLED IDEOGRAPH CONGRATULATION and U+3299 CIRCLED
-- IDEOGRAPH SECRET
-- -------------------------------------------------------------
-- Although these two codepoints sit in the U+3280..U+32B0 BMP block
-- whose other members are in the IN list, they are also @Emoji=Yes@
-- and folded by UTS #46 -- the same cross-tool ambiguity shape as
-- the squared CJK ideograph emoji. Their UTS #46 fold targets
-- (U+795D and U+79D8 respectively) could attract separate
-- registration by an opportunistic third party at any time;
-- empirically there is no live divergence today (the kanji-form
-- .ws labels resolve to NXDOMAIN), but folding by default would
-- bake in a future hijack vector with no signal to the user.
-- Refused symmetrically with the rest of the mappedEmojiRanges
-- set.
--
-- OUT -- Squared kana abbreviations (U+3300..U+3357, 88 entries)
-- -------------------------------------------------------------
-- Each source folds to a multi-character katakana loanword like
-- "calorie", "dollar", or "mansion" -- a complete Japanese word
-- rather than single-label content. The source codepoints exist
-- primarily for vertical-typesetting space-economy in Japanese
-- newspapers, not IME output.
--
-- OUT -- Squared unit / math abbreviations (U+3382..U+33DF range)
-- ---------------------------------------------------------------
-- Each source folds to mixed Greek+Latin or unit-notation sequences
-- like "us", "mhz", "m/s", or "kohm", several of which contain
-- U+2215 (DIVISION SLASH, itself disallowed in IDNA2008). Not
-- plausible label content.
--
-- OUT -- Circled Katakana (U+32D0..U+32FE, 50 entries)
-- ---------------------------------------------------
-- Each source folds to a single katakana. Such labels appear in
-- domain registrations rarely enough that the IME-accident
-- probability for the circled form is lower than the false-positive
-- cost of folding.
--
-- OUT -- Circled Hangul (U+3260..U+327E, 35 entries)
-- --------------------------------------------------
-- Same rationale as circled Katakana; single-jamo / one-syllable
-- forms are not the kind of content registrars routinely accept as
-- whole labels.
--
-- OUT -- Arabic positional forms (U+FB50..U+FDFD and U+FE70..U+FEFC,
-- ~706 entries)
-- ----------------------------------------------------------------
-- Targets are natural Arabic base letters, but the sources are
-- presentation forms not produced by any modern Arabic IME. They
-- appear in user input only from copy-paste out of legacy PDFs that
-- baked shaping into the encoding. IDNA2008 explicitly disallows the
-- source codepoints; we honour that disposition.
--
-- OUT -- Latin compatibility ligatures (U+FB00..U+FB06, 7 entries)
-- ----------------------------------------------------------------
-- Sources fold to ASCII letter sequences (fi, fl, ffi, ffl, ff, st).
-- Copy-paste from typeset documents can produce these, but the
-- population producing such documents is not shopping for domain
-- names, and the targets are ASCII (we reject non-ASCII -> ASCII
-- mappings on principle).
--
-- OUT -- Latin digraphs (U+0132 U+01C4 U+01C7 U+01CA U+01F1 and their
-- title/lower variants, 11 entries)
-- -----------------------------------------------------------------
-- Even rarer than the ligatures; no auto-conversion path puts them
-- in user input.
--
-- OUT -- Math alphanumeric / font-variant Latin and Greek (~439
-- entries)
-- ------------------------------------------------------------
-- Sources are math-italic, math-bold, fraktur, script, and related
-- typesetting alphabets (e.g. U+210F PLANCK CONSTANT OVER TWO PI ->
-- U+0127, U+1D400 MATHEMATICAL BOLD CAPITAL A -> "a"). Math
-- typesetting only; never an IME or text-entry accident.
--
-- OUT -- Roman numerals, vulgar fractions, symbol expansions,
-- superscripts, subscripts, degree-Celsius/Fahrenheit --
-- everything else in the UTS #46 mapping table.
-- ----------------------------------------------------------
-- Examples: U+2122 TRADE MARK SIGN -> "tm", U+2116 NUMERO SIGN ->
-- "no", U+2121 TELEPHONE SIGN -> "tel", U+2175 SMALL ROMAN NUMERAL
-- SIX -> "vi", U+00BC VULGAR FRACTION ONE QUARTER -> "1" U+2044 "4",
-- U+00B2 SUPERSCRIPT TWO -> "2", U+2103 DEGREE CELSIUS -> U+00B0 "C".
-- Either the target isn't plausible domain content (fraction slash,
-- degree sign), or the source requires deliberate insertion via a
-- Unicode picker.
--
-- ---------------------------------------------------------------------------
-- Revision discipline
-- ---------------------------------------------------------------------------
-- Any change to the IN\\/OUT classifications above must carry, in the
-- same commit:
--
-- 1. A clear rationale, written as an extension or amendment of the
-- relevant section of the comment block above — so the standing
-- record of why each entry is where it is stays current and
-- self-contained.
--
-- 2. Test coverage in one or more of the test programs (the unit
-- tests under tests/, the conformance suite, or the idnaparse \\/
-- idnabench harnesses) exercising the new or revised behaviour.
-- ---------------------------------------------------------------------------
-- | Look up @cp@ in the @MAPUTS46@ allow-list. Returns the list of
-- target codepoints to substitute (always non-empty), or 'Nothing'
-- if @cp@ is not in the allow-list.
--
-- The @'Int' -> 'Maybe' ['Int']@ shape mirrors the way UTS #46
-- mappings work: a single source codepoint may expand to one or
-- several target codepoints (era-name codepoints expand 1:2, the
-- circled CJK ideographs expand 1:1).
uts46Lookup :: Int -> Maybe [Int]
uts46Lookup = \case
-- Japanese era
0x32FF -> Just [0x4EE4, 0x548C] -- Reiwa
0x337B -> Just [0x5E73, 0x6210] -- Heisei
0x337C -> Just [0x662D, 0x548C] -- Showa
0x337D -> Just [0x5927, 0x6B63] -- Taisho
0x337E -> Just [0x660E, 0x6CBB] -- Meiji
-- Circled CJK ideographs in U+3244..U+3247
0x3244 -> Just [0x554F] -- question
0x3245 -> Just [0x5E7C] -- kindergarten
0x3246 -> Just [0x6587] -- school
0x3247 -> Just [0x7B8F] -- koto
-- Circled CJK ideographs in U+3280..U+32B0
0x3280 -> Just [0x4E00] -- one
0x3281 -> Just [0x4E8C] -- two
0x3282 -> Just [0x4E09] -- three
0x3283 -> Just [0x56DB] -- four
0x3284 -> Just [0x4E94] -- five
0x3285 -> Just [0x516D] -- six
0x3286 -> Just [0x4E03] -- seven
0x3287 -> Just [0x516B] -- eight
0x3288 -> Just [0x4E5D] -- nine
0x3289 -> Just [0x5341] -- ten
0x328A -> Just [0x6708] -- moon
0x328B -> Just [0x706B] -- fire
0x328C -> Just [0x6C34] -- water
0x328D -> Just [0x6728] -- wood
0x328E -> Just [0x91D1] -- metal
0x328F -> Just [0x571F] -- earth
0x3290 -> Just [0x65E5] -- sun
0x3291 -> Just [0x682A] -- stock
0x3292 -> Just [0x6709] -- have
0x3293 -> Just [0x793E] -- society
0x3294 -> Just [0x540D] -- name
0x3295 -> Just [0x7279] -- special
0x3296 -> Just [0x8CA1] -- financial
-- U+3297, U+3299 deliberately excluded (also mappedEmoji);
-- see the OUT-list rationale block above.
0x3298 -> Just [0x52B4] -- labor
0x329A -> Just [0x7537] -- male
0x329B -> Just [0x5973] -- female
0x329C -> Just [0x9069] -- suitable
0x329D -> Just [0x512A] -- excellent
0x329E -> Just [0x5370] -- print
0x329F -> Just [0x6CE8] -- attention
0x32A0 -> Just [0x9805] -- item
0x32A1 -> Just [0x4F11] -- rest
0x32A2 -> Just [0x5199] -- copy
0x32A3 -> Just [0x6B63] -- correct
0x32A4 -> Just [0x4E0A] -- high
0x32A5 -> Just [0x4E2D] -- centre
0x32A6 -> Just [0x4E0B] -- low
0x32A7 -> Just [0x5DE6] -- left
0x32A8 -> Just [0x53F3] -- right
0x32A9 -> Just [0x533B] -- medicine
0x32AA -> Just [0x5B97] -- religion
0x32AB -> Just [0x5B66] -- study
0x32AC -> Just [0x76E3] -- supervise
0x32AD -> Just [0x4F01] -- enterprise
0x32AE -> Just [0x8CC7] -- resource
0x32AF -> Just [0x5354] -- alliance
0x32B0 -> Just [0x591C] -- night
_ -> Nothing
{-# INLINE uts46Lookup #-}