From 1532c701a822901984e7aecfdf3463fa9a447af0 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 4 Apr 2024 01:20:56 -0700 Subject: [PATCH 01/31] Add baked data for IanaToBcp47MapV2Marker --- components/timezone/src/provider.rs | 2 ++ provider/baked/timezone/data/macros.rs | 5 ++++ .../macros/time_zone_iana_to_bcp47_v2.rs.data | 27 +++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 provider/baked/timezone/data/macros/time_zone_iana_to_bcp47_v2.rs.data diff --git a/components/timezone/src/provider.rs b/components/timezone/src/provider.rs index 5c2fb70991c..bbcc5d299c5 100644 --- a/components/timezone/src/provider.rs +++ b/components/timezone/src/provider.rs @@ -42,6 +42,7 @@ const _: () = { icu_timezone_data::make_provider!(Baked); icu_timezone_data::impl_time_zone_bcp47_to_iana_v1!(Baked); icu_timezone_data::impl_time_zone_iana_to_bcp47_v1!(Baked); + icu_timezone_data::impl_time_zone_iana_to_bcp47_v2!(Baked); icu_timezone_data::impl_time_zone_metazone_period_v1!(Baked); }; @@ -51,6 +52,7 @@ pub const KEYS: &[DataKey] = &[ MetazonePeriodV1Marker::KEY, names::Bcp47ToIanaMapV1Marker::KEY, names::IanaToBcp47MapV1Marker::KEY, + names::IanaToBcp47MapV2Marker::KEY, ]; /// TimeZone ID in BCP47 format diff --git a/provider/baked/timezone/data/macros.rs b/provider/baked/timezone/data/macros.rs index b086025a2af..38ac20849c1 100644 --- a/provider/baked/timezone/data/macros.rs +++ b/provider/baked/timezone/data/macros.rs @@ -36,6 +36,11 @@ mod time_zone_iana_to_bcp47_v1; #[doc(inline)] pub use __impl_time_zone_iana_to_bcp47_v1 as impl_time_zone_iana_to_bcp47_v1; #[macro_use] +#[path = "macros/time_zone_iana_to_bcp47_v2.rs.data"] +mod time_zone_iana_to_bcp47_v2; +#[doc(inline)] +pub use __impl_time_zone_iana_to_bcp47_v2 as impl_time_zone_iana_to_bcp47_v2; +#[macro_use] #[path = "macros/time_zone_metazone_period_v1.rs.data"] mod time_zone_metazone_period_v1; #[doc(inline)] diff --git a/provider/baked/timezone/data/macros/time_zone_iana_to_bcp47_v2.rs.data b/provider/baked/timezone/data/macros/time_zone_iana_to_bcp47_v2.rs.data new file mode 100644 index 00000000000..93e927c47cb --- /dev/null +++ b/provider/baked/timezone/data/macros/time_zone_iana_to_bcp47_v2.rs.data @@ -0,0 +1,27 @@ +// @generated +/// Implement `DataProvider` on the given struct using the data +/// hardcoded in this file. This allows the struct to be used with +/// `icu`'s `_unstable` constructors. +#[doc(hidden)] +#[macro_export] +macro_rules! __impl_time_zone_iana_to_bcp47_v2 { + ($ provider : ty) => { + #[clippy::msrv = "1.67"] + const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO; + #[clippy::msrv = "1.67"] + impl $provider { + #[doc(hidden)] + pub const SINGLETON_TIME_ZONE_IANA_TO_BCP47_V2: &'static ::Yokeable = &icu::timezone::provider::names::IanaToBcp47MapV2 { map: zerotrie::ZeroAsciiIgnoreCaseTrie { store: unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"\xE1sABCEGHIJKLMNPRSTUWZ\x0F\x0F\x0F\x13\x13\x13\x13\x13\x13\x14\x14\x14\x16\x16\x16\x16\x16\x16\x03*\xCE\"KZ\xE4\xF3\xFD\x032E\x1F(29\xD6\xDB\xE1gfmnrstu\x02\t\t\t\r\x0E*+\xAC\xBF\x83\trica/\xE1rABCDEFGHJKLMNOPSTW\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x015v\x99\xC1\xCA\xD3\xDC\xE3\xF7\x1CP\x85\xAC\xB8\xC3\xCC\xE6\xC5bcdls\x07\x0C\x17\x1Eidjan\x91Mcra\x923dis_Ababa\x92\x11giers\x91{m\xC2ae\x04ra\x92\x07ra\x92\x06\xC5ailru\x15\x1B#.\xC2mn\x05ako\x93S\xC2gj\x04ui\x91Gul\x92?ssau\x92Uantyre\x93oazzaville\x91Ijumbura\x90]\xC3aeo\x11\x16\xC2is\x04ro\x92\x03ablanca\x93Cuta\x92\tnakry\x92C\xC3ajo\x14\x1C\xC2kr\x04ar\x95'_es_Salaam\x95Wibouti\x91suala\x91Wl_Aaiun\x92\x05reetown\x95#aborone\x91\rarare\x96u\xC2ou\x0Channesburg\x96qba\x95-\xC3ahi\x07\x0Fmpala\x95]artoum\x95\x15\xC2gn\x05ali\x95\rshasa\x91E\xC4aiou\x05\x0F\x13gos\x94\x17breville\x92%me\x95?\xC3abs\x05\x0Enda\x90\x01umbashi\x91Caka\x96s\xC3abo\x15\x1C\xC3lps\x05\nabo\x92Kuto\x94\reru\x939abane\x957\xC2gn\x08adishu\x95)rovia\x937\xC4adio\x07\x0F\x15irobi\x93\x07jamena\x95;amey\x94\x13uakchott\x93cuagadougou\x90Worto-Novo\x90_ao_Tome\x95/\xC3iru\x08\x0Fmbuktu\x93Ripoli\x93Anis\x95Kindhoek\x94\x0Ferica/\xE1vABCDEFGHIJKLMNOPRSTVWY\0\x01\x01\x02\x02\x02\x02\x02\x03\x03\x03\x03\x04\x04\x04\x05\x05\x06\x06\x06\x06\xE5D\xE7 Ki\xC4\xE1Zr\xAA\xDF\x85\xDF\xE7Z\xA3@z\x8D\xA3\xC5dnrst\x04\x1D\xC5\xCDak\x95e\xC3cgt\x08\x0Ehorage\x95iuilla\x89igua\x87\xC3agu\x08\x9Eguaina\x90ientina/\xC9BCJLMRSTU\r17@HUmuuenos_Aires\x90\x17\xC2ao\ttamarca\x90\x1B\xC2mr\rodRivadavia\x90\x1Adoba\x90\x19ujuy\x90\x1Fa_Rioja\x90\x1Dendoza\x90#io_Gallegos\x90%a\xC2ln\x04ta\x90'_\xC2JL\x05uan\x90+uis\x90!ucuman\x90)shuaia\x90-ba\x90Kuncion\x94O\xC2ik\x07kokan\x91?a\x95d\xC5aelou\x1A&2I\xC2hr\x0Fia\x91\x05_Banderas\x94\x05bados\x90Ql\xC2ei\x03m\x90kze\x91\x11anc-Sablon\x91/\xC3agi\x08\r_Vista\x90mota\x91]se\x95kenos_Aires\x90\x16\xC6ahioruCTa\x82\x89\xC5mnrty\x19\x1E$,\xC2bp\x0Bridge_Bay\x911o_Grande\x90qcun\x93uacas\x96_amarca\x90\x1A\xC2em\x05nne\x92/an\x93\x1Di\xC2ch\x05ago\x95muahua\x93qudad_Juarez\x93s\xC2rs\x14\xC2ad\x0Bl_Harbour\x91>oba\x90\x18ta_Rica\x91_eston\x91\x13\xC2ir\x05aba\x90oacao\x8F\xC3aeo\x1C+\xC2nw\x0Bmarkshavn\x927son\x913_Creek\x915\xC2nt\x05ver\x95oroit\x95qminica\x91w\xC4diln\x08\x10\x1Bmonton\x91\x15runepe\x90s_Salvador\x951senada\x94\x06ort\xC2_a\x11\xC2NW\x07elson\x91\x17ayne\x95tleza\x90w\xC4loru\t\x1B.ace_Bay\x91\x19\xC2do\x06thab\x928se_Bay\x91\x1B\xC2ae\tnd_Turk\x959nada\x92+\xC2ay\x1C\xC3dty\x08\x0Feloupe\x92Eemala\x92Qaquil\x91\x7Fana\x92W\xC2ae\x0F\xC2lv\x06ifax\x91\x1Dana\x91crmosillo\x93w\xC2nqn\xC2dueiana\xC2/pW\xC7IKMPTVW\r\x12\x1A%/Andianapolis\x95unox\x95{arengo\x95getersburg\x96\x19ell_City\x96\x15\xC2ei\x05vay\x95wncennes\x96\rinamac\x96\x17olis\x95tvik\x919aluit\x91\x1F\xC2au\x07maica\x93\x01\xC2jn\x04uy\x90\x1Eeau\x95y\xC3enr!(ntucky/\xC2LM\x0Bouisville\x95\x7Fonticello\x96\x03ox_IN\x95zalendijk\x90g\xC3aio\x06\n_Paz\x90ema\x94-\xC3suw\n\x13_Angeles\x95}isville\x95~er_Princes\x953\xC4aeio;ks\xC5cnrtz\x05\x11\"*eio\x90{a\xC2gu\x04ua\x94\x19s\x90y\xC2it\x05got\x92Ginique\x93aamoros\x93yatlan\x94\x01\xC4nrtx\x10\x15\x1E\xC2do\x05oza\x90\"minee\x96\x01ida\x93}lakatla\x96\x05ico_City\x93{quelon\x94?n\xC2ct\x05ton\x91!\xC3ers\x0F\x14\xC2rv\x05rey\x93\x7Fideo\x96Ueal\x91(errat\x93e\xC5aeiou\x06\x0E\x15Lssau\x91\tw_York\x96\x0Bpigon\x91(\xC2mr\x03e\x96\x0F\xC2ot\x05nha\x90uh_Dakota/\xC3BCN\x07\x0Eeulah\x96\x1Benter\x96\x07ew_Salem\x96\tuk\x929jinaga\x94\x03\xC4ahou\x1E%R\xC2nr\x11\xC2ag\x04ma\x94+nirtung\x91\x1Eamaribo\x95+oenix\x96\x11rt\xC3-_o\x0B\x15au-Prince\x92aof_Spain\x95Q_\xC2AV\x05cre\x90~elho\x90}\xC2en\nrto_Rico\x94Cta_Arenas\x91S\xC4aeio\x190:\xC2in\nny_River\x91,kin_Inlet\x917\xC3cgs\x05\nife\x91\x01ina\x91%olute\x91#o_Branco\x90\x7Fsario\x90\x18\xC6achitw2>FK\x84\xC2no&t\xC3aio\x10\x15\xC2_r\x08Isabel\x94\x06em\x91\x07ago\x91U_Domingo\x91y_Paulo\x91\x03oresbysund\x92;iprock\x95ntka\x96\x13_\xC6BJKLTV\x0B\x11\x17\x1D$arthelemy\x92Iohns\x91'itts\x93\x15ucia\x931homas\x96cincent\x96]ift_Current\x91=\xC4ehio\x0B\x1C#gucigalpa\x92]u\xC2ln\x03e\x92=der_Bay\x91(juana\x94\x07r\xC2ot\x05nto\x91)ola\x96a\xC2ai\tncouver\x91+rgin\x96b\xC2hi\nitehorse\x91;nnipeg\x91-\xC2ae\x07kutat\x96\x1Dllowknife\x91\x14tarctica/\xC8CDMPRSTV\x06\x1D9@H[aasey\x90\x03\xC2au\x05vis\x90\x05montDUrville\x90\x07\xC2ac\x11\xC2cw\x08quarie\x90Eson\x90\tMurdo\x90\x0Balmer\x90\rothera\x90\x0F\xC2oy\nuth_Pole\x94$owa\x90\x11roll\x90\x13ostok\x90\x15ctic/Longyearbyen\x95\x1Fia/\xE1uABCDFGHIJKMNOPQRSTUVY\0\0\0\0\0\0\x01\x01\x01\x01\x01\x02\x02\x02\x02\x02\x02\x03\x03\x03G\x87\xC1\xEF\xF9\xFE+>\\\xCE\xFB\x1D)Kgx\xBF\tBZ\xC7dlmnqst\x04\n\x0F\x15!3en\x96mmaty\x93#man\x93\x03adyr\x94_t\xC2ao\x03u\x93\x1Fbe\x93!h\xC2gk\x06abat\x95Ihabad\x95Hyrau\x93%\xC4aeir%+2\xC5ghknr\x06\x0C\x0F\x15hdad\x92urain\x90[u\x90Mgkok\x95Anaul\x94[irut\x93/shkek\x93\tunei\x90c\xC3aho\x08-lcutta\x92p\xC3iou\x04\x17ta\x94]\xC2in\x08balsan\x93Wgqing\x91Xngking\x91Xlombo\x935\xC4ahiu\x0F\x14\x18\xC2cm\x04ca\x90Rascus\x955aka\x90Sli\x95G\xC2bs\x03ai\x83hanbe\x95Camagusta\x91iaza\x92'\xC3aeo\x06\x0Crbin\x91Xbron\x92Y\xC3_nv\n\x12Chi_Minh\x96eg_Kong\x92[d\x93Y\xC2rs\x07kutsk\x94ctanbul\x95N\xC2ae\x11\xC2ky\x06arta\x92gapura\x92erusalem\x92}\xC5ahoru2:AL\xC5bmrst\x03\x0B\x11\x17ul\x85chatka\x94wachi\x94;hgar\x91Z\xC2hm\x07mandu\x94\x1Fandu\x94\x1Eandyga\x94glkata\x92qasnoyarsk\x94i\xC3acw\x0B\x11la_Lumpur\x94\x0Bhing\x94\tait\x93\x1B\xC2au#\xC4cgkn\t\x0F\x16a\xC2ou\x02\x93\\\x93]adan\x94aassar\x92iila\x949scat\x94)\xC2io\x07cosia\x91kvo\xC2ks\tuznetsk\x94qibirsk\x94u\xC2mr\x04sk\x94sal\x93+\xC3hoy\n\x13nom_Penh\x93\x0Bntianak\x92kongyang\x93\x17\xC3aoy\x05\rtar\x94Qstanay\x93'zylorda\x93)\xC2ai\x07ngoon\x93Tyadh\x95\x0F\xC5aehir\x1A\x1F'0\xC3ikm\x05\x0Cgon\x96dhalin\x95\x03arkand\x96Woul\x93\x19anghai\x91Yngapore\x95\x19ednekolymsk\x94{\xC5abeho\x10\x17'4\xC2is\x05pei\x95Uhkent\x96Yilisi\x92-\xC2hl\x05ran\x92w_Aviv\x92|im\xC2bp\x03u\x91\nhu\x91\x0B\xC2km\x04yo\x93\x05sk\x94}\xC4jlrs\r#)ung_Pandang\x92ha\xC2an\tnbaatar\x93[_Bator\x93Zumqi\x91[t-Nera\x95\x01\xC2il\tentiane\x93-adivostok\x95\x07\xC2ae\x0F\xC2kn\x06utsk\x95\x0Bgon\x93U\xC2kr\x0Caterinburg\x95\tevan\x8Dlantic/\xC8ABCFJMRS\x07\x0F\"0:BLzores\x94Kermuda\x90aa\xC2np\x05ary\x92\x0Be_Verde\x91ea\xC2er\x05roe\x92 oe\x92!an_Mayen\x95\x1Eadeira\x94Geykjavik\x92y\xC2ot\ruth_Georgia\x92O\xC2_a\x08Helena\x95\x1Bnley\x92\x19stralia/\xD0ABCDEHLMNPQSTVWY\x0F%7>DKeo{\x81\x8C\x9B\xA4\xAD\xB2\xC2Cd\x03T\x90Helaide\x903r\xC2io\x07sbane\x907ken_Hill\x905\xC2au\x08nberra\x90Hrrie\x90Mao\xC2lr\x10\xC2ae\x05ska\x95hutian\x95dizona\x96\x10entral\x95last\xC2-e\tIndiana\x95trn\x96\nawaii\x95rndiana-Starke\x95z\xC2io\x08chigan\x95puntain\x95nacific\x95|-New\x95|amoa\x90.C\x96\x1E-SU\x94nulu\x96\x1E") } }, bcp47_ids: unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"adalv\0\0\0aedxb\0\0\0afkbl\0\0\0aganu\0\0\0aiaxa\0\0\0altia\0\0\0amevn\0\0\0ancur\0\0\0aolad\0\0\0aqcas\0\0\0aqdav\0\0\0aqddu\0\0\0aqmaw\0\0\0aqmcm\0\0\0aqplm\0\0\0aqrot\0\0\0aqsyw\0\0\0aqtrl\0\0\0aqvos\0\0\0arbue\0\0\0arcor\0\0\0arctc\0\0\0arirj\0\0\0arjuj\0\0\0arluq\0\0\0armdz\0\0\0arrgl\0\0\0arsla\0\0\0artuc\0\0\0aruaq\0\0\0arush\0\0\0asppg\0\0\0atvie\0\0\0auadl\0\0\0aubhq\0\0\0aubne\0\0\0audrw\0\0\0aueuc\0\0\0auhba\0\0\0auldc\0\0\0auldh\0\0\0aumel\0\0\0aumqi\0\0\0auper\0\0\0ausyd\0\0\0awaua\0\0\0azbak\0\0\0basjj\0\0\0bbbgi\0\0\0bddac\0\0\0bebru\0\0\0bfoua\0\0\0bgsof\0\0\0bhbah\0\0\0bibjm\0\0\0bjptn\0\0\0bmbda\0\0\0bnbwn\0\0\0bolpb\0\0\0bqkra\0\0\0braux\0\0\0brbel\0\0\0brbvb\0\0\0brcgb\0\0\0brcgr\0\0\0brern\0\0\0brfen\0\0\0brfor\0\0\0brmao\0\0\0brmcz\0\0\0brpvh\0\0\0brrbr\0\0\0brrec\0\0\0brsao\0\0\0brssa\0\0\0brstm\0\0\0bsnas\0\0\0btthi\0\0\0bwgbe\0\0\0bymsq\0\0\0bzbze\0\0\0cacfq\0\0\0caedm\0\0\0cafne\0\0\0caglb\0\0\0cagoo\0\0\0cahal\0\0\0caiql\0\0\0camon\0\0\0careb\0\0\0careg\0\0\0casjf\0\0\0cator\0\0\0cavan\0\0\0cawnp\0\0\0caybx\0\0\0caycb\0\0\0cayda\0\0\0caydq\0\0\0cayek\0\0\0cayev\0\0\0cayxy\0\0\0cayyn\0\0\0cayzs\0\0\0cccck\0\0\0cdfbm\0\0\0cdfih\0\0\0cfbgf\0\0\0cgbzv\0\0\0chzrh\0\0\0ciabj\0\0\0ckrar\0\0\0clipc\0\0\0clpuq\0\0\0clscl\0\0\0cmdla\0\0\0cnsha\0\0\0cnurc\0\0\0cobog\0\0\0crsjo\0\0\0cst6cdt\0cuhav\0\0\0cvrai\0\0\0cxxch\0\0\0cyfmg\0\0\0cynic\0\0\0czprg\0\0\0deber\0\0\0debsngn\0djjib\0\0\0dkcph\0\0\0dmdom\0\0\0dosdq\0\0\0dzalg\0\0\0ecgps\0\0\0ecgye\0\0\0eetll\0\0\0egcai\0\0\0eheai\0\0\0erasm\0\0\0esceu\0\0\0eslpa\0\0\0esmad\0\0\0est5edt\0etadd\0\0\0fihel\0\0\0fimhq\0\0\0fjsuv\0\0\0fkpsy\0\0\0fmksa\0\0\0fmpni\0\0\0fmtkk\0\0\0fotho\0\0\0frpar\0\0\0galbv\0\0\0gazastrpgblon\0\0\0gdgnd\0\0\0getbs\0\0\0gfcay\0\0\0gggci\0\0\0ghacc\0\0\0gigib\0\0\0gldkshvnglgoh\0\0\0globy\0\0\0glthu\0\0\0gmbjl\0\0\0gmt\0\0\0\0\0gncky\0\0\0gpbbr\0\0\0gpmsb\0\0\0gpsbh\0\0\0gqssg\0\0\0grath\0\0\0gsgrv\0\0\0gtgua\0\0\0gugum\0\0\0gwoxb\0\0\0gygeo\0\0\0hebron\0\0hkhkg\0\0\0hntgu\0\0\0hrzag\0\0\0htpap\0\0\0hubud\0\0\0iddjj\0\0\0idjkt\0\0\0idmak\0\0\0idpnk\0\0\0iedub\0\0\0imdgs\0\0\0inccu\0\0\0iodga\0\0\0iqbgw\0\0\0irthr\0\0\0isrey\0\0\0itrom\0\0\0jeruslm\0jesth\0\0\0jmkin\0\0\0joamm\0\0\0jptyo\0\0\0kenbo\0\0\0kgfru\0\0\0khpnh\0\0\0kicxi\0\0\0kipho\0\0\0kitrw\0\0\0kmyva\0\0\0knbas\0\0\0kpfnj\0\0\0krsel\0\0\0kwkwi\0\0\0kygec\0\0\0kzaau\0\0\0kzakx\0\0\0kzala\0\0\0kzguw\0\0\0kzksn\0\0\0kzkzo\0\0\0kzura\0\0\0lavte\0\0\0lbbey\0\0\0lccas\0\0\0livdz\0\0\0lkcmb\0\0\0lrmlw\0\0\0lsmsu\0\0\0ltvno\0\0\0lulux\0\0\0lvrix\0\0\0lytip\0\0\0macas\0\0\0mcmon\0\0\0mdkiv\0\0\0metgd\0\0\0mgtnr\0\0\0mhkwa\0\0\0mhmaj\0\0\0mkskp\0\0\0mlbko\0\0\0mmrgn\0\0\0mncoq\0\0\0mnhvd\0\0\0mnuln\0\0\0momfm\0\0\0mpspn\0\0\0mqfdf\0\0\0mrnkc\0\0\0msmni\0\0\0mst7mdt\0mtmla\0\0\0muplu\0\0\0mvmle\0\0\0mwblz\0\0\0mxchi\0\0\0mxcjs\0\0\0mxcun\0\0\0mxhmo\0\0\0mxmam\0\0\0mxmex\0\0\0mxmid\0\0\0mxmty\0\0\0mxmzt\0\0\0mxoji\0\0\0mxpvr\0\0\0mxtij\0\0\0mykch\0\0\0mykul\0\0\0mzmpm\0\0\0nawdh\0\0\0ncnou\0\0\0nenim\0\0\0nfnlk\0\0\0nglos\0\0\0nimga\0\0\0nlams\0\0\0noosl\0\0\0npktm\0\0\0nrinu\0\0\0nuiue\0\0\0nzakl\0\0\0nzcht\0\0\0ommct\0\0\0papty\0\0\0pelim\0\0\0pfgmr\0\0\0pfnhv\0\0\0pfppt\0\0\0pgpom\0\0\0pgraw\0\0\0phmnl\0\0\0pkkhi\0\0\0plwaw\0\0\0pmmqc\0\0\0pnpcn\0\0\0prsju\0\0\0pst8pdt\0ptfnc\0\0\0ptlis\0\0\0ptpdl\0\0\0pwror\0\0\0pyasu\0\0\0qadoh\0\0\0rereu\0\0\0robuh\0\0\0rsbeg\0\0\0ruasf\0\0\0rubax\0\0\0ruchita\0rudyr\0\0\0rugdx\0\0\0ruikt\0\0\0rukgd\0\0\0rukhndg\0rukra\0\0\0rukuf\0\0\0rukvx\0\0\0rumow\0\0\0runoz\0\0\0ruoms\0\0\0ruovb\0\0\0rupkc\0\0\0rurtw\0\0\0rusred\0\0rutof\0\0\0ruuly\0\0\0ruunera\0ruuus\0\0\0ruvog\0\0\0ruvvo\0\0\0ruyek\0\0\0ruyks\0\0\0rwkgl\0\0\0saruh\0\0\0sbhir\0\0\0scmaw\0\0\0sdkrt\0\0\0sesto\0\0\0sgsin\0\0\0shshn\0\0\0silju\0\0\0sjlyr\0\0\0skbts\0\0\0slfna\0\0\0smsai\0\0\0sndkr\0\0\0somgq\0\0\0srpbm\0\0\0ssjub\0\0\0sttms\0\0\0svsal\0\0\0sxphi\0\0\0sydam\0\0\0szqmn\0\0\0tcgdt\0\0\0tdndj\0\0\0tfpfr\0\0\0tglfw\0\0\0thbkk\0\0\0tjdyu\0\0\0tkfko\0\0\0tldil\0\0\0tmasb\0\0\0tntun\0\0\0totbu\0\0\0trist\0\0\0ttpos\0\0\0tvfun\0\0\0twtpe\0\0\0tzdar\0\0\0uaiev\0\0\0uasip\0\0\0ugkla\0\0\0umawk\0\0\0ummdy\0\0\0unk\0\0\0\0\0usadk\0\0\0usaeg\0\0\0usanc\0\0\0usboi\0\0\0uschi\0\0\0usden\0\0\0usdet\0\0\0ushnl\0\0\0usind\0\0\0usinvev\0usjnu\0\0\0usknx\0\0\0uslax\0\0\0uslui\0\0\0usmnm\0\0\0usmoc\0\0\0usmtm\0\0\0usndcnt\0usndnsl\0usnyc\0\0\0usoea\0\0\0usome\0\0\0usphx\0\0\0ussit\0\0\0ustel\0\0\0uswlz\0\0\0uswsq\0\0\0usxul\0\0\0usyak\0\0\0utc\0\0\0\0\0utce01\0\0utce02\0\0utce03\0\0utce04\0\0utce05\0\0utce06\0\0utce07\0\0utce08\0\0utce09\0\0utce10\0\0utce11\0\0utce12\0\0utce13\0\0utce14\0\0utcw01\0\0utcw02\0\0utcw03\0\0utcw04\0\0utcw05\0\0utcw06\0\0utcw07\0\0utcw08\0\0utcw09\0\0utcw10\0\0utcw11\0\0utcw12\0\0uymvd\0\0\0uzskd\0\0\0uztas\0\0\0vavat\0\0\0vcsvd\0\0\0veccs\0\0\0vgtov\0\0\0vistt\0\0\0vnsgn\0\0\0vuvli\0\0\0wfmau\0\0\0wsapw\0\0\0yeade\0\0\0ytmam\0\0\0zajnb\0\0\0zmlun\0\0\0zwhre\0\0\0") }, bcp47_ids_checksum: 4342095620703458995u64 }; + } + #[clippy::msrv = "1.67"] + impl icu_provider::DataProvider for $provider { + fn load(&self, req: icu_provider::DataRequest) -> Result, icu_provider::DataError> { + if req.locale.is_empty() { + Ok(icu_provider::DataResponse { payload: Some(icu_provider::DataPayload::from_static_ref(Self::SINGLETON_TIME_ZONE_IANA_TO_BCP47_V2)), metadata: Default::default() }) + } else { + Err(icu_provider::DataErrorKind::ExtraneousLocale.with_req(::KEY, req)) + } + } + } + }; +} From f4845a9db88ebe5350e1a5767fcd6067d7218c55 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 4 Apr 2024 01:18:28 -0700 Subject: [PATCH 02/31] Add new TimeZoneIdMapper type --- components/timezone/src/ids.rs | 367 +++++++++++++++++++++++++++++++++ components/timezone/src/lib.rs | 2 + utils/zerotrie/src/cursor.rs | 5 +- 3 files changed, 371 insertions(+), 3 deletions(-) create mode 100644 components/timezone/src/ids.rs diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs new file mode 100644 index 00000000000..d22245a894f --- /dev/null +++ b/components/timezone/src/ids.rs @@ -0,0 +1,367 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +use alloc::borrow::Cow; +use icu_provider::prelude::*; +use zerotrie::cursor::ZeroAsciiIgnoreCaseTrieCursor; + +use crate::{ + provider::names::{IanaToBcp47MapV2, IanaToBcp47MapV2Marker}, + TimeZoneBcp47Id, +}; + +/// A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. +/// +/// This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. +/// It also supports normalizing and canonicalizing the IANA strings. +/// +/// # Examples +/// +/// ``` +/// use icu::timezone::TimeZoneIdMapper; +/// +/// let mapper = TimeZoneIdMapper::new(); +/// let mapper = mapper.as_borrowed(); +/// +/// // The IANA zone "Australia/Melbourne" is the BCP-47 zone "aumel": +/// assert_eq!( +/// mapper.iana_to_bcp47("Australia/Melbourne"), +/// Some("aumel".parse().unwrap()) +/// ); +/// +/// // Lookup is ASCII-case insensitive: +/// assert_eq!( +/// mapper.iana_to_bcp47("australia/melbourne"), +/// Some("aumel".parse().unwrap()) +/// ); +/// +/// // The IANA zone "Australia/Victoria" is an alias: +/// assert_eq!( +/// mapper.iana_to_bcp47("Australia/Victoria"), +/// Some("aumel".parse().unwrap()) +/// ); +/// +/// // We can recover the canonical name from the mapper: +/// assert_eq!( +/// mapper +/// .canonicalize_iana("Australia/Victoria") +/// .unwrap() +/// .string, +/// "Australia/Melbourne" +/// ); +/// ``` +#[derive(Debug, Clone)] +pub struct TimeZoneIdMapper { + data: DataPayload, +} + +impl TimeZoneIdMapper { + /// Creates a new [`TimeZoneIdMapper`] using compiled data. + /// + /// See [`TimeZoneIdMapper`] for an example. + /// + /// ✨ *Enabled with the `compiled_data` Cargo feature.* + /// + /// [📚 Help choosing a constructor](icu_provider::constructors) + #[cfg(feature = "compiled_data")] + pub fn new() -> Self { + Self { + data: DataPayload::from_static_ref( + crate::provider::Baked::SINGLETON_TIME_ZONE_IANA_TO_BCP47_V2, + ), + } + } + + icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: DataError, + #[cfg(skip)] + functions: [ + new, + try_new_with_any_provider, + try_new_with_buffer_provider, + try_new_unstable, + Self, + ] + ); + + #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)] + pub fn try_new_unstable

(provider: &P) -> Result + where + P: DataProvider + ?Sized, + { + let data = provider.load(Default::default())?.take_payload()?; + Ok(Self { data }) + } + + /// Returns a borrowed version of the mapper that can be queried. + /// + /// This avoids a small potential cost of reading the data pointer. + pub fn as_borrowed(&self) -> TimeZoneIdMapperBorrowed { + TimeZoneIdMapperBorrowed { + data: self.data.get(), + } + } +} + +/// A borrowed wrapper around the time zone ID mapper, returned by +/// [`TimeZoneIdMapper::as_borrowed()`]. More efficient to query. +#[derive(Debug, Copy, Clone)] +pub struct TimeZoneIdMapperBorrowed<'a> { + data: &'a IanaToBcp47MapV2<'a>, +} + +impl<'a> TimeZoneIdMapperBorrowed<'a> { + /// Gets the BCP-47 time zone ID from an IANA time zone ID + /// with a case-insensitive lookup. + /// + /// # Examples + /// + /// ``` + /// use icu_timezone::TimeZoneBcp47Id; + /// use icu_timezone::TimeZoneIdMapper; + /// use tinystr::tinystr; + /// + /// let mapper = TimeZoneIdMapper::new(); + /// let mapper = mapper.as_borrowed(); + /// + /// assert_eq!( + /// mapper.iana_to_bcp47("AMERICA/chicago"), + /// Some(TimeZoneBcp47Id(tinystr!(8, "uschi"))) + /// ); + /// ``` + pub fn iana_to_bcp47(&self, iana_id: &str) -> Option { + self.iana_lookup_quick(iana_id) + .and_then(|trie_value| self.data.bcp47_ids.get(trie_value.index())) + } + + /// Normalizes the syntax of an IANA time zone ID. + /// + /// Also returns the BCP-47 time zone ID. + /// + /// # Examples + /// + /// ``` + /// use icu_timezone::TimeZoneBcp47Id; + /// use icu_timezone::TimeZoneIdMapper; + /// use tinystr::tinystr; + /// + /// let mapper = TimeZoneIdMapper::new(); + /// let mapper = mapper.as_borrowed(); + /// + /// let normalized = mapper.normalize_iana("AMERICA/chicago").unwrap(); + /// + /// assert_eq!(normalized.string, "America/Chicago"); + /// assert_eq!(normalized.bcp47_id, TimeZoneBcp47Id(tinystr!(8, "uschi"))); + /// ``` + pub fn normalize_iana<'s>(&self, iana_id: &'s str) -> Option> { + let Some((trie_value, string)) = self.iana_lookup_with_normalization(iana_id, |_| {}) + else { + return None; + }; + let Some(bcp47_id) = self.data.bcp47_ids.get(trie_value.index()) else { + debug_assert!(false, "index should be in range"); + return None; + }; + Some(NormalizedIana { string, bcp47_id }) + } + + /// Returns the canonical, normalized name of the given IANA time zone. + /// + /// Also returns the BCP-47 time zone ID. + /// + /// # Examples + /// + /// ``` + /// use icu_timezone::TimeZoneBcp47Id; + /// use icu_timezone::TimeZoneIdMapper; + /// use tinystr::tinystr; + /// + /// let mapper = TimeZoneIdMapper::new(); + /// let mapper = mapper.as_borrowed(); + /// + /// let canonicalized = mapper.canonicalize_iana("Asia/Calcutta").unwrap(); + /// + /// assert_eq!(canonicalized.string, "Asia/Kolkata"); + /// assert_eq!( + /// canonicalized.bcp47_id, + /// TimeZoneBcp47Id(tinystr!(8, "inccu")) + /// ); + /// ``` + pub fn canonicalize_iana<'s>(&self, iana_id: &'s str) -> Option> { + // Note: We collect the cursors into a stack so that we start probing + // nearby the input IANA name. This should improve lookup time since + // most renames share the same prefix like "Asia" or "Europe". + let mut stack = Vec::with_capacity(iana_id.len()); + let Some((trie_value, mut string)) = self.iana_lookup_with_normalization(iana_id, |cursor| { + stack.push((cursor.clone(), 0, 1)); + }) else { + return None; + }; + let Some(bcp47_id) = self.data.bcp47_ids.get(trie_value.index()) else { + debug_assert!(false, "index should be in range"); + return None; + }; + if trie_value.is_canonical() { + return Some(NormalizedIana { string, bcp47_id }); + } + // If we get here, we need to walk the trie to find the canonical IANA ID. + loop { + let Some((mut cursor, index, suffix_len)) = stack.pop() else { + // Nothing left in the trie. + debug_assert!(false, "every time zone should have a canonical IANA ID"); + return None; + }; + // Check to see if there is a value at the current node. + if let Some(candidate) = cursor.take_value().map(IanaTrieValue) { + if candidate.index() == trie_value.index() && candidate.is_canonical() { + // Success! Found what we were looking for. + break; + } + } + // Now check for children of the current node. + let mut sub_cursor = cursor.clone(); + if let Some(probe_result) = sub_cursor.probe(index) { + // Found a child. Add the current byte edge to the string. + if !probe_result.byte.is_ascii() { + debug_assert!(false, "non-ASCII probe byte: {}", probe_result.byte); + return None; + } + // Safety: the byte being added is ASCII as guarded above + unsafe { string.to_mut().as_mut_vec().push(probe_result.byte) }; + // Add the child to the stack, and also add back the current + // node if there are more siblings to visit. + if index + 1 < probe_result.total_siblings as usize { + stack.push((cursor, index + 1, suffix_len)); + stack.push((sub_cursor, 0, 1)); + } else { + stack.push((sub_cursor, 0, suffix_len + 1)); + } + } else { + // No more children. Pop this node's bytes from the string. + for _ in 0..suffix_len { + // Safety: we check that the bytes being removed are ASCII + let removed_byte = unsafe { string.to_mut().as_mut_vec().pop() }; + if let Some(removed_byte) = removed_byte { + if !removed_byte.is_ascii() { + debug_assert!(false, "non-ASCII removed byte: {removed_byte}"); + return None; + } + } else { + debug_assert!(false, "could not remove another byte"); + return None; + } + } + } + } + Some(NormalizedIana { string, bcp47_id }) + } + + /// Returns the canonical, normalized IANA ID of the given BCP-47 ID. + /// + /// Only use this function if you don't have the IANA ID. [`Self::canonicalize_iana()`] + /// is much faster in the common case. + /// + /// # Examples + /// + /// ``` + /// use icu_timezone::TimeZoneBcp47Id; + /// use icu_timezone::TimeZoneIdMapper; + /// use tinystr::tinystr; + /// + /// let mapper = TimeZoneIdMapper::new(); + /// let mapper = mapper.as_borrowed(); + /// + /// let canonicalized = mapper.canonicalize_iana("Asia/Calcutta").unwrap(); + /// + /// assert_eq!(canonicalized.string, "Asia/Kolkata"); + /// assert_eq!( + /// canonicalized.bcp47_id, + /// TimeZoneBcp47Id(tinystr!(8, "inccu")) + /// ); + /// ``` + pub fn bcp47_to_iana_search(&self, bcp47_id: TimeZoneBcp47Id) -> Option { + // TODO: This is not as efficient as .probe() since it allocates a string each time. + for (string, raw_value) in self.data.map.iter() { + let trie_value = IanaTrieValue(raw_value); + if !trie_value.is_canonical() { + continue; + } + let Some(candidate_bcp47_id) = self.data.bcp47_ids.get(trie_value.index()) else { + debug_assert!(false, "index should be in range"); + return None; + }; + if candidate_bcp47_id == bcp47_id { + return Some(string); + } + } + None + } + + fn iana_lookup_quick(&self, iana_id: &str) -> Option { + self.data.map.get(iana_id).map(IanaTrieValue) + } + + fn iana_lookup_with_normalization<'l, 's>( + &'l self, + iana_id: &'s str, + mut cursor_fn: impl FnMut(&ZeroAsciiIgnoreCaseTrieCursor<'l>), + ) -> Option<(IanaTrieValue, Cow<'s, str>)> { + let mut cursor = self.data.map.cursor(); + let mut string = Cow::Borrowed(iana_id); + let mut i = 0; + let trie_value = loop { + cursor_fn(&cursor); + let Some(input_byte) = string.as_bytes().get(i).copied() else { + break cursor.take_value().map(IanaTrieValue); + }; + let Some(matched_byte) = cursor.step(input_byte) else { + break None; + }; + if matched_byte != input_byte { + // Safety: we write to input_byte farther down after performing safety checks. + let Some(input_byte) = unsafe { string.to_mut().as_bytes_mut() }.get_mut(i) else { + debug_assert!(false, "the same index was just accessed earlier"); + break None; + }; + if !input_byte.is_ascii() { + debug_assert!(false, "non-ASCII input byte: {input_byte}"); + break None; + } + if !matched_byte.is_ascii() { + debug_assert!(false, "non-ASCII matched byte: {matched_byte}"); + break None; + } + // Safety: we just checked that both input_byte and matched_byte are ASCII, + // so the buffer remains UTF-8 when we replace one with the other. + *input_byte = matched_byte; + } + i += 1; + }?; + Some((trie_value, string)) + } +} + +/// A wrapper around a syntax-normalized IANA time zone identifier string +/// and its corresponding BCP-47 time zone identifier. +#[derive(Debug)] +pub struct NormalizedIana<'s> { + /// The syntax-normalized IANA time zone identifier string. + pub string: Cow<'s, str>, + /// The corresponding BCP-47 time zone identifier. + pub bcp47_id: TimeZoneBcp47Id, +} + +#[derive(Copy, Clone)] +#[repr(transparent)] +struct IanaTrieValue(usize); + +impl IanaTrieValue { + #[inline] + pub fn index(self) -> usize { + self.0 >> 1 + } + #[inline] + pub fn is_canonical(self) -> bool { + (self.0 & 0x1) != 0 + } +} diff --git a/components/timezone/src/lib.rs b/components/timezone/src/lib.rs index d2724e52464..9c502222528 100644 --- a/components/timezone/src/lib.rs +++ b/components/timezone/src/lib.rs @@ -127,6 +127,7 @@ extern crate alloc; mod error; mod iana_ids; +mod ids; mod metazone; pub mod provider; mod time_zone; @@ -134,6 +135,7 @@ mod types; pub use error::TimeZoneError; pub use iana_ids::{IanaBcp47RoundTripMapper, IanaToBcp47Mapper}; +pub use ids::{NormalizedIana, TimeZoneIdMapper, TimeZoneIdMapperBorrowed}; pub use metazone::MetazoneCalculator; pub use provider::{MetazoneId, TimeZoneBcp47Id}; pub use time_zone::CustomTimeZone; diff --git a/utils/zerotrie/src/cursor.rs b/utils/zerotrie/src/cursor.rs index 9a947422d60..95b8ef755ad 100644 --- a/utils/zerotrie/src/cursor.rs +++ b/utils/zerotrie/src/cursor.rs @@ -365,10 +365,9 @@ impl<'a> ZeroAsciiIgnoreCaseTrieCursor<'a> { /// let mut key_str = Cow::Borrowed("abc".as_bytes()); /// let mut i = 0; /// let value = loop { - /// if i == key_str.len() { + /// let Some(input_byte) = key_str.get(i).copied() else { /// break cursor.take_value(); - /// } - /// let input_byte = key_str[i]; + /// }; /// let Some(matched_byte) = cursor.step(input_byte) else { /// break None; /// }; From 4f3bbb4ff97226e9930fdc4e968a0038aaeeb3dd Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 4 Apr 2024 01:27:50 -0700 Subject: [PATCH 03/31] Add test in datagen --- .../src/transform/cldr/time_zones/names.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/provider/datagen/src/transform/cldr/time_zones/names.rs b/provider/datagen/src/transform/cldr/time_zones/names.rs index e95533aa5c2..4a0083e6981 100644 --- a/provider/datagen/src/transform/cldr/time_zones/names.rs +++ b/provider/datagen/src/transform/cldr/time_zones/names.rs @@ -243,3 +243,31 @@ fn test_compute_bcp47_ids_hash() { assert_ne!(checksum4, checksum5); assert_ne!(checksum4, checksum6); } + +/// Tests that all IANA time zone IDs normalize and canonicalize to their correct form. +#[test] +fn test_normalize_canonicalize_iana_coverage() { + let provider = crate::DatagenProvider::new_testing(); + + let resource: &cldr_serde::time_zones::bcp47_tzid::Resource = provider + .cldr() + .unwrap() + .bcp47() + .read_and_parse("timezone.json") + .unwrap(); + let iana2bcp = &compute_bcp47_tzids_btreemap(&resource.keyword.u.time_zones.values); + + let mapper = icu_timezone::TimeZoneIdMapper::try_new_unstable(&provider).unwrap(); + let mapper = mapper.as_borrowed(); + + for iana_id in iana2bcp.keys() { + let normalized = mapper.normalize_iana(&iana_id).unwrap().string; + assert_eq!(&normalized, iana_id); + } + + let bcp2iana = compute_canonical_tzids_btreemap(&resource.keyword.u.time_zones.values); + for (iana_id, bcp47_id) in iana2bcp.iter() { + let canonicalized = mapper.canonicalize_iana(&iana_id).unwrap().string; + assert_eq!(&canonicalized, bcp2iana.get(bcp47_id).unwrap()); + } +} From 1dfdc26816773565cdc9ab9154ee0b80a9b93730 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 4 Apr 2024 01:36:17 -0700 Subject: [PATCH 04/31] fmt --- components/timezone/src/ids.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index d22245a894f..1a3525f0e25 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -192,9 +192,11 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { // nearby the input IANA name. This should improve lookup time since // most renames share the same prefix like "Asia" or "Europe". let mut stack = Vec::with_capacity(iana_id.len()); - let Some((trie_value, mut string)) = self.iana_lookup_with_normalization(iana_id, |cursor| { - stack.push((cursor.clone(), 0, 1)); - }) else { + let Some((trie_value, mut string)) = + self.iana_lookup_with_normalization(iana_id, |cursor| { + stack.push((cursor.clone(), 0, 1)); + }) + else { return None; }; let Some(bcp47_id) = self.data.bcp47_ids.get(trie_value.index()) else { From 4ea5be8285b1ee00b6cd1725aad446e7cc42553e Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 4 Apr 2024 01:37:17 -0700 Subject: [PATCH 05/31] features --- components/timezone/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/timezone/Cargo.toml b/components/timezone/Cargo.toml index 72f5536bb2b..03733821a6f 100644 --- a/components/timezone/Cargo.toml +++ b/components/timezone/Cargo.toml @@ -25,7 +25,7 @@ icu_calendar = { workspace = true } icu_locid = { workspace = true } icu_provider = { workspace = true, features = ["macros"] } tinystr = { workspace = true, features = ["alloc", "zerovec"] } -zerotrie = { workspace = true, features = ["yoke", "zerofrom"] } +zerotrie = { workspace = true, features = ["alloc", "yoke", "zerofrom"] } zerovec = { workspace = true, features = ["derive", "yoke"] } databake = { workspace = true, optional = true, features = ["derive"] } From 90824c98776b7bb2e0088843269788791025122b Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 4 Apr 2024 12:06:10 -0500 Subject: [PATCH 06/31] Update ids.rs --- components/timezone/src/ids.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index 1a3525f0e25..f4ff0cbadf2 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -3,6 +3,7 @@ // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use alloc::borrow::Cow; +use alloc::string::String; use icu_provider::prelude::*; use zerotrie::cursor::ZeroAsciiIgnoreCaseTrieCursor; From 107173c73f1b7089667338fcd6241ea751f4d7a5 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 4 Apr 2024 12:22:16 -0500 Subject: [PATCH 07/31] Update ids.rs --- components/timezone/src/ids.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index f4ff0cbadf2..f03f6323612 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -4,6 +4,7 @@ use alloc::borrow::Cow; use alloc::string::String; +use alloc::vec::Vec; use icu_provider::prelude::*; use zerotrie::cursor::ZeroAsciiIgnoreCaseTrieCursor; From 4b2516a612874080229837cbf6ababeec8c34d3d Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 00:54:41 -0700 Subject: [PATCH 08/31] Reduce allocations and DRY --- components/timezone/src/ids.rs | 161 ++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 72 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index f03f6323612..0bdda49ef68 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -194,11 +194,9 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { // nearby the input IANA name. This should improve lookup time since // most renames share the same prefix like "Asia" or "Europe". let mut stack = Vec::with_capacity(iana_id.len()); - let Some((trie_value, mut string)) = - self.iana_lookup_with_normalization(iana_id, |cursor| { - stack.push((cursor.clone(), 0, 1)); - }) - else { + let Some((trie_value, string)) = self.iana_lookup_with_normalization(iana_id, |cursor| { + stack.push((cursor.clone(), 0, 1)); + }) else { return None; }; let Some(bcp47_id) = self.data.bcp47_ids.get(trie_value.index()) else { @@ -209,55 +207,15 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { return Some(NormalizedIana { string, bcp47_id }); } // If we get here, we need to walk the trie to find the canonical IANA ID. - loop { - let Some((mut cursor, index, suffix_len)) = stack.pop() else { - // Nothing left in the trie. - debug_assert!(false, "every time zone should have a canonical IANA ID"); - return None; - }; - // Check to see if there is a value at the current node. - if let Some(candidate) = cursor.take_value().map(IanaTrieValue) { - if candidate.index() == trie_value.index() && candidate.is_canonical() { - // Success! Found what we were looking for. - break; - } - } - // Now check for children of the current node. - let mut sub_cursor = cursor.clone(); - if let Some(probe_result) = sub_cursor.probe(index) { - // Found a child. Add the current byte edge to the string. - if !probe_result.byte.is_ascii() { - debug_assert!(false, "non-ASCII probe byte: {}", probe_result.byte); - return None; - } - // Safety: the byte being added is ASCII as guarded above - unsafe { string.to_mut().as_mut_vec().push(probe_result.byte) }; - // Add the child to the stack, and also add back the current - // node if there are more siblings to visit. - if index + 1 < probe_result.total_siblings as usize { - stack.push((cursor, index + 1, suffix_len)); - stack.push((sub_cursor, 0, 1)); - } else { - stack.push((sub_cursor, 0, suffix_len + 1)); - } - } else { - // No more children. Pop this node's bytes from the string. - for _ in 0..suffix_len { - // Safety: we check that the bytes being removed are ASCII - let removed_byte = unsafe { string.to_mut().as_mut_vec().pop() }; - if let Some(removed_byte) = removed_byte { - if !removed_byte.is_ascii() { - debug_assert!(false, "non-ASCII removed byte: {removed_byte}"); - return None; - } - } else { - debug_assert!(false, "could not remove another byte"); - return None; - } - } - } - } - Some(NormalizedIana { string, bcp47_id }) + let needle = trie_value.to_canonical(); + let Some(string) = self.iana_search(needle, string.into_owned(), stack) else { + debug_assert!(false, "every time zone should have a canonical IANA ID"); + return None; + }; + Some(NormalizedIana { + string: Cow::Owned(string), + bcp47_id, + }) } /// Returns the canonical, normalized IANA ID of the given BCP-47 ID. @@ -284,27 +242,21 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// ); /// ``` pub fn bcp47_to_iana_search(&self, bcp47_id: TimeZoneBcp47Id) -> Option { - // TODO: This is not as efficient as .probe() since it allocates a string each time. - for (string, raw_value) in self.data.map.iter() { - let trie_value = IanaTrieValue(raw_value); - if !trie_value.is_canonical() { - continue; - } - let Some(candidate_bcp47_id) = self.data.bcp47_ids.get(trie_value.index()) else { - debug_assert!(false, "index should be in range"); - return None; - }; - if candidate_bcp47_id == bcp47_id { - return Some(string); - } - } - None + let index = self.data.bcp47_ids.binary_search(&bcp47_id).ok()?; + let stack = alloc::vec![(self.data.map.cursor(), 0, 0)]; + let needle = IanaTrieValue::canonical_for_index(index); + let string = self.iana_search(needle, String::new(), stack)?; + Some(string) } + /// Queries the data for `iana_id` without recording the normalized string. + /// This is a fast, no-alloc lookup. fn iana_lookup_quick(&self, iana_id: &str) -> Option { self.data.map.get(iana_id).map(IanaTrieValue) } + /// Queries the data for `iana_id` while keeping track of the normalized string. + /// This is a fast lookup, but it may require allocating memory. fn iana_lookup_with_normalization<'l, 's>( &'l self, iana_id: &'s str, @@ -343,6 +295,63 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { }?; Some((trie_value, string)) } + + /// Performs a reverse lookup by walking the trie with an optional start position. + /// This is not a fast operation since it requires a linear search. + fn iana_search( + &self, + needle: IanaTrieValue, + mut string: String, + mut stack: Vec<(ZeroAsciiIgnoreCaseTrieCursor, usize, usize)>, + ) -> Option { + loop { + let Some((mut cursor, index, suffix_len)) = stack.pop() else { + // Nothing left in the trie. + return None; + }; + // Check to see if there is a value at the current node. + if let Some(candidate) = cursor.take_value().map(IanaTrieValue) { + if candidate == needle { + // Success! Found what we were looking for. + return Some(string); + } + } + // Now check for children of the current node. + let mut sub_cursor = cursor.clone(); + if let Some(probe_result) = sub_cursor.probe(index) { + // Found a child. Add the current byte edge to the string. + if !probe_result.byte.is_ascii() { + debug_assert!(false, "non-ASCII probe byte: {}", probe_result.byte); + return None; + } + // Safety: the byte being added is ASCII as guarded above + unsafe { string.as_mut_vec().push(probe_result.byte) }; + // Add the child to the stack, and also add back the current + // node if there are more siblings to visit. + if index + 1 < probe_result.total_siblings as usize { + stack.push((cursor, index + 1, suffix_len)); + stack.push((sub_cursor, 0, 1)); + } else { + stack.push((sub_cursor, 0, suffix_len + 1)); + } + } else { + // No more children. Pop this node's bytes from the string. + for _ in 0..suffix_len { + // Safety: we check that the bytes being removed are ASCII + let removed_byte = unsafe { string.as_mut_vec().pop() }; + if let Some(removed_byte) = removed_byte { + if !removed_byte.is_ascii() { + debug_assert!(false, "non-ASCII removed byte: {removed_byte}"); + return None; + } + } else { + debug_assert!(false, "could not remove another byte"); + return None; + } + } + } + } + } } /// A wrapper around a syntax-normalized IANA time zone identifier string @@ -355,17 +364,25 @@ pub struct NormalizedIana<'s> { pub bcp47_id: TimeZoneBcp47Id, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq)] #[repr(transparent)] struct IanaTrieValue(usize); impl IanaTrieValue { #[inline] - pub fn index(self) -> usize { + pub(crate) fn to_canonical(self) -> Self { + Self(self.0 | 1) + } + #[inline] + pub(crate) fn canonical_for_index(index: usize) -> Self { + Self(index << 1).to_canonical() + } + #[inline] + pub(crate) fn index(self) -> usize { self.0 >> 1 } #[inline] - pub fn is_canonical(self) -> bool { + pub(crate) fn is_canonical(self) -> bool { (self.0 & 0x1) != 0 } } From bf1132716ebf349f8bfad3c75d0cd2619caa7961 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 11:15:03 -0700 Subject: [PATCH 09/31] Update utils/zerotrie/src/cursor.rs Co-authored-by: Robert Bastian <4706271+robertbastian@users.noreply.github.com> --- utils/zerotrie/src/cursor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/zerotrie/src/cursor.rs b/utils/zerotrie/src/cursor.rs index 95b8ef755ad..71f668fd209 100644 --- a/utils/zerotrie/src/cursor.rs +++ b/utils/zerotrie/src/cursor.rs @@ -365,7 +365,7 @@ impl<'a> ZeroAsciiIgnoreCaseTrieCursor<'a> { /// let mut key_str = Cow::Borrowed("abc".as_bytes()); /// let mut i = 0; /// let value = loop { - /// let Some(input_byte) = key_str.get(i).copied() else { + /// let Some(&input_byte) = key_str.get(i) else { /// break cursor.take_value(); /// }; /// let Some(matched_byte) = cursor.step(input_byte) else { From 8886642d45a4e8c30f0aed0ec55be6dae269cb0a Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 11:15:24 -0700 Subject: [PATCH 10/31] Update components/timezone/src/ids.rs Co-authored-by: Robert Bastian <4706271+robertbastian@users.noreply.github.com> --- components/timezone/src/ids.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index 0bdda49ef68..ae76c541e2a 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -32,7 +32,7 @@ use crate::{ /// Some("aumel".parse().unwrap()) /// ); /// -/// // Lookup is ASCII-case insensitive: +/// // Lookup is ASCII-case-insensitive: /// assert_eq!( /// mapper.iana_to_bcp47("australia/melbourne"), /// Some("aumel".parse().unwrap()) From b9bc0445e57706efe577bdeb1d0082c0151d086e Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 11:05:57 -0700 Subject: [PATCH 11/31] Improve examples; rename function --- components/timezone/src/ids.rs | 52 +++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index ae76c541e2a..e58b67968f1 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -116,6 +116,8 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// Gets the BCP-47 time zone ID from an IANA time zone ID /// with a case-insensitive lookup. /// + /// Returns `None` if the IANA ID is not found. + /// /// # Examples /// /// ``` @@ -127,9 +129,12 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let mapper = mapper.as_borrowed(); /// /// assert_eq!( - /// mapper.iana_to_bcp47("AMERICA/chicago"), - /// Some(TimeZoneBcp47Id(tinystr!(8, "uschi"))) + /// mapper.iana_to_bcp47("Asia/CALCUTTA"), + /// Some(TimeZoneBcp47Id(tinystr!(8, "inccu"))) /// ); + /// + /// // Unknown IANA time zone ID: + /// assert_eq!(mapper.iana_to_bcp47("America/San_Francisco"), None); /// ``` pub fn iana_to_bcp47(&self, iana_id: &str) -> Option { self.iana_lookup_quick(iana_id) @@ -140,6 +145,8 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// /// Also returns the BCP-47 time zone ID. /// + /// Returns `None` if the IANA ID is not found. + /// /// # Examples /// /// ``` @@ -150,10 +157,13 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let mapper = TimeZoneIdMapper::new(); /// let mapper = mapper.as_borrowed(); /// - /// let normalized = mapper.normalize_iana("AMERICA/chicago").unwrap(); + /// let result = mapper.normalize_iana("Asia/CALCUTTA").unwrap(); /// - /// assert_eq!(normalized.string, "America/Chicago"); - /// assert_eq!(normalized.bcp47_id, TimeZoneBcp47Id(tinystr!(8, "uschi"))); + /// assert_eq!(result.string, "Asia/Calcutta"); + /// assert_eq!(result.bcp47_id, TimeZoneBcp47Id(tinystr!(8, "inccu"))); + /// + /// // Unknown IANA time zone ID: + /// assert_eq!(mapper.normalize_iana("America/San_Francisco"), None); /// ``` pub fn normalize_iana<'s>(&self, iana_id: &'s str) -> Option> { let Some((trie_value, string)) = self.iana_lookup_with_normalization(iana_id, |_| {}) @@ -171,6 +181,8 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// /// Also returns the BCP-47 time zone ID. /// + /// Returns `None` if the IANA ID is not found. + /// /// # Examples /// /// ``` @@ -181,13 +193,13 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let mapper = TimeZoneIdMapper::new(); /// let mapper = mapper.as_borrowed(); /// - /// let canonicalized = mapper.canonicalize_iana("Asia/Calcutta").unwrap(); + /// let result = mapper.canonicalize_iana("Asia/CALCUTTA").unwrap(); /// - /// assert_eq!(canonicalized.string, "Asia/Kolkata"); - /// assert_eq!( - /// canonicalized.bcp47_id, - /// TimeZoneBcp47Id(tinystr!(8, "inccu")) - /// ); + /// assert_eq!(result.string, "Asia/Kolkata"); + /// assert_eq!(result.bcp47_id, TimeZoneBcp47Id(tinystr!(8, "inccu"))); + /// + /// // Unknown IANA time zone ID: + /// assert_eq!(mapper.canonicalize_iana("America/San_Francisco"), None); /// ``` pub fn canonicalize_iana<'s>(&self, iana_id: &'s str) -> Option> { // Note: We collect the cursors into a stack so that we start probing @@ -223,6 +235,8 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// Only use this function if you don't have the IANA ID. [`Self::canonicalize_iana()`] /// is much faster in the common case. /// + /// Returns `None` if the BCP-47 ID is not found. + /// /// # Examples /// /// ``` @@ -233,15 +247,15 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let mapper = TimeZoneIdMapper::new(); /// let mapper = mapper.as_borrowed(); /// - /// let canonicalized = mapper.canonicalize_iana("Asia/Calcutta").unwrap(); + /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "inccu")); + /// let result = mapper.find_canonical_iana_from_bcp47(bcp47_id).unwrap(); /// - /// assert_eq!(canonicalized.string, "Asia/Kolkata"); - /// assert_eq!( - /// canonicalized.bcp47_id, - /// TimeZoneBcp47Id(tinystr!(8, "inccu")) - /// ); + /// assert_eq!(result, "Asia/Kolkata"); + /// + /// // Unknown BCP-47 time zone ID: + /// assert_eq!(mapper.find_canonical_iana_from_bcp47(TimeZoneBcp47Id(tinystr!(8, "ussfo"))), None); /// ``` - pub fn bcp47_to_iana_search(&self, bcp47_id: TimeZoneBcp47Id) -> Option { + pub fn find_canonical_iana_from_bcp47(&self, bcp47_id: TimeZoneBcp47Id) -> Option { let index = self.data.bcp47_ids.binary_search(&bcp47_id).ok()?; let stack = alloc::vec![(self.data.map.cursor(), 0, 0)]; let needle = IanaTrieValue::canonical_for_index(index); @@ -356,7 +370,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// A wrapper around a syntax-normalized IANA time zone identifier string /// and its corresponding BCP-47 time zone identifier. -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct NormalizedIana<'s> { /// The syntax-normalized IANA time zone identifier string. pub string: Cow<'s, str>, From 87f339c98ca724b12f501b632d4eec88f56c6995 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 11:15:53 -0700 Subject: [PATCH 12/31] impl Deref for TimeZoneBcp47Id --- components/timezone/src/ids.rs | 17 +++++++---------- components/timezone/src/provider.rs | 9 +++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index e58b67968f1..92bb8517d84 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -123,15 +123,13 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// ``` /// use icu_timezone::TimeZoneBcp47Id; /// use icu_timezone::TimeZoneIdMapper; - /// use tinystr::tinystr; /// /// let mapper = TimeZoneIdMapper::new(); /// let mapper = mapper.as_borrowed(); /// - /// assert_eq!( - /// mapper.iana_to_bcp47("Asia/CALCUTTA"), - /// Some(TimeZoneBcp47Id(tinystr!(8, "inccu"))) - /// ); + /// let result = mapper.iana_to_bcp47("Asia/CALCUTTA").unwrap(); + /// + /// assert_eq!(*result, "inccu"); /// /// // Unknown IANA time zone ID: /// assert_eq!(mapper.iana_to_bcp47("America/San_Francisco"), None); @@ -152,7 +150,6 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// ``` /// use icu_timezone::TimeZoneBcp47Id; /// use icu_timezone::TimeZoneIdMapper; - /// use tinystr::tinystr; /// /// let mapper = TimeZoneIdMapper::new(); /// let mapper = mapper.as_borrowed(); @@ -160,7 +157,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let result = mapper.normalize_iana("Asia/CALCUTTA").unwrap(); /// /// assert_eq!(result.string, "Asia/Calcutta"); - /// assert_eq!(result.bcp47_id, TimeZoneBcp47Id(tinystr!(8, "inccu"))); + /// assert_eq!(*result.bcp47_id, "inccu"); /// /// // Unknown IANA time zone ID: /// assert_eq!(mapper.normalize_iana("America/San_Francisco"), None); @@ -188,7 +185,6 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// ``` /// use icu_timezone::TimeZoneBcp47Id; /// use icu_timezone::TimeZoneIdMapper; - /// use tinystr::tinystr; /// /// let mapper = TimeZoneIdMapper::new(); /// let mapper = mapper.as_borrowed(); @@ -196,7 +192,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let result = mapper.canonicalize_iana("Asia/CALCUTTA").unwrap(); /// /// assert_eq!(result.string, "Asia/Kolkata"); - /// assert_eq!(result.bcp47_id, TimeZoneBcp47Id(tinystr!(8, "inccu"))); + /// assert_eq!(*result.bcp47_id, "inccu"); /// /// // Unknown IANA time zone ID: /// assert_eq!(mapper.canonicalize_iana("America/San_Francisco"), None); @@ -253,7 +249,8 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// assert_eq!(result, "Asia/Kolkata"); /// /// // Unknown BCP-47 time zone ID: - /// assert_eq!(mapper.find_canonical_iana_from_bcp47(TimeZoneBcp47Id(tinystr!(8, "ussfo"))), None); + /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "ussfo")); + /// assert_eq!(mapper.find_canonical_iana_from_bcp47(bcp47_id), None); /// ``` pub fn find_canonical_iana_from_bcp47(&self, bcp47_id: TimeZoneBcp47Id) -> Option { let index = self.data.bcp47_ids.binary_search(&bcp47_id).ok()?; diff --git a/components/timezone/src/provider.rs b/components/timezone/src/provider.rs index bbcc5d299c5..259510e3ff9 100644 --- a/components/timezone/src/provider.rs +++ b/components/timezone/src/provider.rs @@ -15,6 +15,7 @@ //! //! Read more about data providers: [`icu_provider`] +use core::ops::Deref; use core::str::FromStr; use icu_provider::prelude::*; use tinystr::TinyAsciiStr; @@ -87,6 +88,14 @@ impl From for TinyAsciiStr<8> { } } +impl Deref for TimeZoneBcp47Id { + type Target = TinyAsciiStr<8>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + impl AsULE for TimeZoneBcp47Id { type ULE = Self; From 66355bb3138a54d706c895391c0666abd3debad6 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 11:16:02 -0700 Subject: [PATCH 13/31] Safety --- components/timezone/src/ids.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index 92bb8517d84..360c0be5c17 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -353,6 +353,9 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { if let Some(removed_byte) = removed_byte { if !removed_byte.is_ascii() { debug_assert!(false, "non-ASCII removed byte: {removed_byte}"); + // If we get here for some reason, `string` is not in a valid state, + // so to be extra safe, we can clear it. + string.clear(); return None; } } else { From 3da059245e63aa3cfe0c3a81b169edf812f62c18 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 11:16:59 -0700 Subject: [PATCH 14/31] Apply bf1132716ebf349f8bfad3c75d0cd2619caa7961 to call site --- components/timezone/src/ids.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index 360c0be5c17..d4adb038c78 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -278,7 +278,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { let mut i = 0; let trie_value = loop { cursor_fn(&cursor); - let Some(input_byte) = string.as_bytes().get(i).copied() else { + let Some(&input_byte) = string.as_bytes().get(i) else { break cursor.take_value().map(IanaTrieValue); }; let Some(matched_byte) = cursor.step(input_byte) else { From 8fab0679d2711132895d4eebcdfafeaf69d3b554 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 11:21:05 -0700 Subject: [PATCH 15/31] Return NormalizedIana --- components/timezone/src/ids.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index d4adb038c78..df572035b43 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -246,18 +246,22 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "inccu")); /// let result = mapper.find_canonical_iana_from_bcp47(bcp47_id).unwrap(); /// - /// assert_eq!(result, "Asia/Kolkata"); + /// assert_eq!(result.string, "Asia/Kolkata"); + /// assert_eq!(*result.bcp47_id, "inccu"); /// /// // Unknown BCP-47 time zone ID: /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "ussfo")); /// assert_eq!(mapper.find_canonical_iana_from_bcp47(bcp47_id), None); /// ``` - pub fn find_canonical_iana_from_bcp47(&self, bcp47_id: TimeZoneBcp47Id) -> Option { + pub fn find_canonical_iana_from_bcp47(&self, bcp47_id: TimeZoneBcp47Id) -> Option> { let index = self.data.bcp47_ids.binary_search(&bcp47_id).ok()?; let stack = alloc::vec![(self.data.map.cursor(), 0, 0)]; let needle = IanaTrieValue::canonical_for_index(index); let string = self.iana_search(needle, String::new(), stack)?; - Some(string) + Some(NormalizedIana { + string: Cow::Owned(string), + bcp47_id, + }) } /// Queries the data for `iana_id` without recording the normalized string. From 28ee49ba8c52ae567fb4139c70817a41f519696a Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 14:35:44 -0700 Subject: [PATCH 16/31] Add TimeZoneIdMapperWithFastCanonicalization --- components/timezone/src/ids.rs | 270 ++++++++++++++++++++++++++++++++- components/timezone/src/lib.rs | 5 +- 2 files changed, 268 insertions(+), 7 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index df572035b43..05a867ce60b 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -9,8 +9,10 @@ use icu_provider::prelude::*; use zerotrie::cursor::ZeroAsciiIgnoreCaseTrieCursor; use crate::{ - provider::names::{IanaToBcp47MapV2, IanaToBcp47MapV2Marker}, - TimeZoneBcp47Id, + provider::names::{ + Bcp47ToIanaMapV1, Bcp47ToIanaMapV1Marker, IanaToBcp47MapV2, IanaToBcp47MapV2Marker, + }, + TimeZoneBcp47Id, TimeZoneError, }; /// A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. @@ -97,7 +99,7 @@ impl TimeZoneIdMapper { /// Returns a borrowed version of the mapper that can be queried. /// - /// This avoids a small potential cost of reading the data pointer. + /// This avoids a small potential indirection cost when querying the mapper. pub fn as_borrowed(&self) -> TimeZoneIdMapperBorrowed { TimeZoneIdMapperBorrowed { data: self.data.get(), @@ -105,6 +107,13 @@ impl TimeZoneIdMapper { } } +impl AsRef for TimeZoneIdMapper { + #[inline] + fn as_ref(&self) -> &TimeZoneIdMapper { + self + } +} + /// A borrowed wrapper around the time zone ID mapper, returned by /// [`TimeZoneIdMapper::as_borrowed()`]. More efficient to query. #[derive(Debug, Copy, Clone)] @@ -150,6 +159,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// ``` /// use icu_timezone::TimeZoneBcp47Id; /// use icu_timezone::TimeZoneIdMapper; + /// use std::borrow::Cow; /// /// let mapper = TimeZoneIdMapper::new(); /// let mapper = mapper.as_borrowed(); @@ -157,8 +167,14 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let result = mapper.normalize_iana("Asia/CALCUTTA").unwrap(); /// /// assert_eq!(result.string, "Asia/Calcutta"); + /// assert!(matches!(result.string, Cow::Owned(_))); /// assert_eq!(*result.bcp47_id, "inccu"); /// + /// // Borrows when able: + /// let result = mapper.normalize_iana("America/Chicago").unwrap(); + /// assert_eq!(result.string, "America/Chicago"); + /// assert!(matches!(result.string, Cow::Borrowed(_))); + /// /// // Unknown IANA time zone ID: /// assert_eq!(mapper.normalize_iana("America/San_Francisco"), None); /// ``` @@ -185,6 +201,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// ``` /// use icu_timezone::TimeZoneBcp47Id; /// use icu_timezone::TimeZoneIdMapper; + /// use std::borrow::Cow; /// /// let mapper = TimeZoneIdMapper::new(); /// let mapper = mapper.as_borrowed(); @@ -192,8 +209,14 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let result = mapper.canonicalize_iana("Asia/CALCUTTA").unwrap(); /// /// assert_eq!(result.string, "Asia/Kolkata"); + /// assert!(matches!(result.string, Cow::Owned(_))); /// assert_eq!(*result.bcp47_id, "inccu"); /// + /// // Borrows when able: + /// let result = mapper.canonicalize_iana("America/Chicago").unwrap(); + /// assert_eq!(result.string, "America/Chicago"); + /// assert!(matches!(result.string, Cow::Borrowed(_))); + /// /// // Unknown IANA time zone ID: /// assert_eq!(mapper.canonicalize_iana("America/San_Francisco"), None); /// ``` @@ -228,8 +251,12 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// Returns the canonical, normalized IANA ID of the given BCP-47 ID. /// - /// Only use this function if you don't have the IANA ID. [`Self::canonicalize_iana()`] - /// is much faster in the common case. + /// This function performs a slow linear search. If this is problematic, consider one of the + /// following functions instead: + /// + /// 1. [`TimeZoneIdMapperBorrowed::canonicalize_iana()`] is faster if you have an IANA ID. + /// 2. [`TimeZoneIdMapperWithFastCanonicalizationBorrowed::canonical_iana_from_bcp47()`] is faster, but it requires + /// loading additional data. /// /// Returns `None` if the BCP-47 ID is not found. /// @@ -238,6 +265,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// ``` /// use icu_timezone::TimeZoneBcp47Id; /// use icu_timezone::TimeZoneIdMapper; + /// use std::borrow::Cow; /// use tinystr::tinystr; /// /// let mapper = TimeZoneIdMapper::new(); @@ -247,13 +275,17 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let result = mapper.find_canonical_iana_from_bcp47(bcp47_id).unwrap(); /// /// assert_eq!(result.string, "Asia/Kolkata"); + /// assert!(matches!(result.string, Cow::Owned(_))); /// assert_eq!(*result.bcp47_id, "inccu"); /// /// // Unknown BCP-47 time zone ID: /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "ussfo")); /// assert_eq!(mapper.find_canonical_iana_from_bcp47(bcp47_id), None); /// ``` - pub fn find_canonical_iana_from_bcp47(&self, bcp47_id: TimeZoneBcp47Id) -> Option> { + pub fn find_canonical_iana_from_bcp47( + &self, + bcp47_id: TimeZoneBcp47Id, + ) -> Option> { let index = self.data.bcp47_ids.binary_search(&bcp47_id).ok()?; let stack = alloc::vec![(self.data.map.cursor(), 0, 0)]; let needle = IanaTrieValue::canonical_for_index(index); @@ -372,6 +404,232 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { } } +/// A mapper that supplements [`TimeZoneIdMapper`] with additional data to improve the performance +/// of canonical IANA ID lookup. +#[derive(Debug, Clone)] +pub struct TimeZoneIdMapperWithFastCanonicalization { + inner: I, + data: DataPayload, +} + +impl TimeZoneIdMapperWithFastCanonicalization { + /// Creates a new [`TimeZoneIdMapperWithFastCanonicalization`] using compiled data. + /// + /// See [`TimeZoneIdMapperWithFastCanonicalization`] for an example. + /// + /// ✨ *Enabled with the `compiled_data` Cargo feature.* + /// + /// [📚 Help choosing a constructor](icu_provider::constructors) + #[cfg(feature = "compiled_data")] + pub fn new() -> Self { + const _: () = assert!( + crate::provider::Baked::SINGLETON_TIME_ZONE_IANA_TO_BCP47_V2.bcp47_ids_checksum + == crate::provider::Baked::SINGLETON_TIME_ZONE_BCP47_TO_IANA_V1.bcp47_ids_checksum, + ); + Self { + inner: TimeZoneIdMapper { + data: DataPayload::from_static_ref( + crate::provider::Baked::SINGLETON_TIME_ZONE_IANA_TO_BCP47_V2, + ), + }, + data: DataPayload::from_static_ref( + crate::provider::Baked::SINGLETON_TIME_ZONE_BCP47_TO_IANA_V1, + ), + } + } + + icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: TimeZoneError, + #[cfg(skip)] + functions: [ + new, + try_new_with_any_provider, + try_new_with_buffer_provider, + try_new_unstable, + Self, + ] + ); + + #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)] + pub fn try_new_unstable

(provider: &P) -> Result + where + P: DataProvider + DataProvider + ?Sized, + { + let mapper = TimeZoneIdMapper::try_new_unstable(provider)?; + Self::try_new_with_mapper_unstable(provider, mapper) + } +} + +impl TimeZoneIdMapperWithFastCanonicalization +where + I: AsRef, +{ + /// Creates a new [`TimeZoneIdMapperWithFastCanonicalization`] using compiled data + /// and a pre-existing [`TimeZoneIdMapper`], which can be borrowed. + /// + /// See [`TimeZoneIdMapperWithFastCanonicalization`] for an example. + /// + /// ✨ *Enabled with the `compiled_data` Cargo feature.* + /// + /// [📚 Help choosing a constructor](icu_provider::constructors) + #[cfg(feature = "compiled_data")] + pub fn try_new_with_mapper(mapper: I) -> Result { + Self { + inner: mapper, + data: DataPayload::from_static_ref( + crate::provider::Baked::SINGLETON_TIME_ZONE_BCP47_TO_IANA_V1, + ), + } + .validated() + } + + icu_provider::gen_any_buffer_data_constructors!(locale: skip, mapper: I, error: TimeZoneError, + #[cfg(skip)] + functions: [ + try_new_with_mapper, + try_new_with_mapper_with_any_provider, + try_new_with_mapper_with_buffer_provider, + try_new_with_mapper_unstable, + Self, + ] + ); + + #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)] + pub fn try_new_with_mapper_unstable

(provider: &P, mapper: I) -> Result + where + P: DataProvider + DataProvider + ?Sized, + { + let data = provider.load(Default::default())?.take_payload()?; + Self { + inner: mapper, + data, + } + .validated() + } + + fn validated(self) -> Result { + if self.inner.as_ref().data.get().bcp47_ids_checksum != self.data.get().bcp47_ids_checksum { + return Err(TimeZoneError::MismatchedChecksums); + } + Ok(self) + } + + /// Gets the inner [`TimeZoneIdMapper`] for performing queries. + pub fn inner(&self) -> &TimeZoneIdMapper { + self.inner.as_ref() + } + + /// Returns a borrowed version of the mapper that can be queried. + /// + /// This avoids a small potential indirection cost when querying the mapper. + pub fn as_borrowed(&self) -> TimeZoneIdMapperWithFastCanonicalizationBorrowed { + TimeZoneIdMapperWithFastCanonicalizationBorrowed { + inner: self.inner.as_ref().as_borrowed(), + data: self.data.get(), + } + } +} + +/// A borrowed wrapper around the time zone ID mapper, returned by +/// [`TimeZoneIdMapperWithFastCanonicalization::as_borrowed()`]. More efficient to query. +#[derive(Debug, Copy, Clone)] +pub struct TimeZoneIdMapperWithFastCanonicalizationBorrowed<'a> { + inner: TimeZoneIdMapperBorrowed<'a>, + data: &'a Bcp47ToIanaMapV1<'a>, +} + +impl<'a> TimeZoneIdMapperWithFastCanonicalizationBorrowed<'a> { + /// Gets the inner [`TimeZoneIdMapperBorrowed`] for performing queries. + pub fn inner(&self) -> TimeZoneIdMapperBorrowed<'a> { + self.inner + } + + /// Returns the canonical, normalized name of the given IANA time zone. + /// + /// Also returns the BCP-47 time zone ID. + /// + /// This is a faster version of [`TimeZoneIdMapperBorrowed::canonicalize_iana()`] + /// and it always returns borrowed IANA strings, but it requires loading additional data. + /// + /// Returns `None` if the IANA ID is not found. + /// + /// # Examples + /// + /// ``` + /// use icu_timezone::TimeZoneBcp47Id; + /// use icu_timezone::TimeZoneIdMapperWithFastCanonicalization; + /// use std::borrow::Cow; + /// + /// let mapper = TimeZoneIdMapperWithFastCanonicalization::new(); + /// let mapper = mapper.as_borrowed(); + /// + /// let result = mapper.canonicalize_iana("Asia/CALCUTTA").unwrap(); + /// + /// // The Cow is always returned borrowed: + /// assert_eq!(result.string, "Asia/Kolkata"); + /// assert!(matches!(result.string, Cow::Borrowed(_))); + /// assert_eq!(*result.bcp47_id, "inccu"); + /// + /// // Unknown IANA time zone ID: + /// assert_eq!(mapper.canonicalize_iana("America/San_Francisco"), None); + /// ``` + pub fn canonicalize_iana(&self, iana_id: &str) -> Option { + let trie_value = self.inner.iana_lookup_quick(iana_id)?; + let Some(bcp47_id) = self.inner.data.bcp47_ids.get(trie_value.index()) else { + debug_assert!(false, "index should be in range"); + return None; + }; + self.get_with_index(trie_value.index(), bcp47_id) + } + + /// Returns the canonical, normalized IANA ID of the given BCP-47 ID. + /// + /// This is a faster version of [`TimeZoneIdMapperBorrowed::find_canonical_iana_from_bcp47()`] + /// and it always returns borrowed IANA strings, but it requires loading additional data. + /// + /// Returns `None` if the BCP-47 ID is not found. + /// + /// # Examples + /// + /// ``` + /// use icu_timezone::TimeZoneBcp47Id; + /// use icu_timezone::TimeZoneIdMapperWithFastCanonicalization; + /// use std::borrow::Cow; + /// use tinystr::tinystr; + /// + /// let mapper = TimeZoneIdMapperWithFastCanonicalization::new(); + /// let mapper = mapper.as_borrowed(); + /// + /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "inccu")); + /// let result = mapper.canonical_iana_from_bcp47(bcp47_id).unwrap(); + /// + /// // The Cow is always returned borrowed: + /// assert_eq!(result.string, "Asia/Kolkata"); + /// assert!(matches!(result.string, Cow::Borrowed(_))); + /// assert_eq!(*result.bcp47_id, "inccu"); + /// + /// // Unknown BCP-47 time zone ID: + /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "ussfo")); + /// assert_eq!(mapper.canonical_iana_from_bcp47(bcp47_id), None); + /// ``` + pub fn canonical_iana_from_bcp47(&self, bcp47_id: TimeZoneBcp47Id) -> Option { + let index = self.inner.data.bcp47_ids.binary_search(&bcp47_id).ok()?; + self.get_with_index(index, bcp47_id) + } + + fn get_with_index(&self, index: usize, bcp47_id: TimeZoneBcp47Id) -> Option { + match self.data.canonical_iana_ids.get(index) { + Some(iana_id) => Some(NormalizedIana { + string: Cow::Borrowed(iana_id), + bcp47_id, + }), + None => { + debug_assert!(false, "index should be in range"); + None + } + } + } +} + /// A wrapper around a syntax-normalized IANA time zone identifier string /// and its corresponding BCP-47 time zone identifier. #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/components/timezone/src/lib.rs b/components/timezone/src/lib.rs index dab92bdfe33..41af4704697 100644 --- a/components/timezone/src/lib.rs +++ b/components/timezone/src/lib.rs @@ -138,7 +138,10 @@ pub use iana_ids::{ IanaBcp47RoundTripMapper, IanaBcp47RoundTripMapperBorrowed, IanaToBcp47Mapper, IanaToBcp47MapperBorrowed, }; -pub use ids::{NormalizedIana, TimeZoneIdMapper, TimeZoneIdMapperBorrowed}; +pub use ids::{ + NormalizedIana, TimeZoneIdMapper, TimeZoneIdMapperBorrowed, + TimeZoneIdMapperWithFastCanonicalization, TimeZoneIdMapperWithFastCanonicalizationBorrowed, +}; pub use metazone::MetazoneCalculator; pub use provider::{MetazoneId, TimeZoneBcp47Id}; pub use time_zone::CustomTimeZone; From 2d879c314eb6ad4463f6e80c0dd402ee5958a840 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 15:01:50 -0700 Subject: [PATCH 17/31] Deprecate and add FFI --- components/timezone/src/iana_ids.rs | 6 + components/timezone/src/ids.rs | 8 +- components/timezone/src/lib.rs | 1 + ffi/capi/bindings/c/ICU4XCustomTimeZone.h | 3 + ffi/capi/bindings/c/ICU4XError.h | 1 + ffi/capi/bindings/c/ICU4XTimeZoneIdMapper.h | 40 ++++ ...TimeZoneIdMapperWithFastCanonicalization.h | 38 ++++ ...ult_box_ICU4XTimeZoneIdMapper_ICU4XError.h | 26 +++ ffi/capi/bindings/cpp/ICU4XCustomTimeZone.h | 3 + ffi/capi/bindings/cpp/ICU4XCustomTimeZone.hpp | 20 ++ ffi/capi/bindings/cpp/ICU4XError.h | 1 + ffi/capi/bindings/cpp/ICU4XError.hpp | 9 + ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.h | 40 ++++ .../bindings/cpp/ICU4XTimeZoneIdMapper.hpp | 196 +++++++++++++++++ ...TimeZoneIdMapperWithFastCanonicalization.h | 38 ++++ ...meZoneIdMapperWithFastCanonicalization.hpp | 164 ++++++++++++++ ...ult_box_ICU4XTimeZoneIdMapper_ICU4XError.h | 26 +++ ffi/capi/bindings/dart/CustomTimeZone.g.dart | 22 ++ ffi/capi/bindings/dart/Error.g.dart | 6 + .../bindings/dart/TimeZoneIdMapper.g.dart | 130 +++++++++++ ...oneIdMapperWithFastCanonicalization.g.dart | 110 ++++++++++ ffi/capi/bindings/dart/lib.g.dart | 2 + ffi/capi/bindings/js/ICU4XCustomTimeZone.d.ts | 10 + ffi/capi/bindings/js/ICU4XCustomTimeZone.mjs | 20 ++ ffi/capi/bindings/js/ICU4XError.d.ts | 7 + ffi/capi/bindings/js/ICU4XError.mjs | 3 + .../bindings/js/ICU4XTimeZoneIdMapper.d.ts | 49 +++++ .../bindings/js/ICU4XTimeZoneIdMapper.mjs | 123 +++++++++++ ...eZoneIdMapperWithFastCanonicalization.d.ts | 43 ++++ ...meZoneIdMapperWithFastCanonicalization.mjs | 102 +++++++++ ffi/capi/bindings/js/index.d.ts | 2 + ffi/capi/bindings/js/index.mjs | 2 + ffi/capi/src/errors.rs | 10 +- ffi/capi/src/iana_bcp47_mapper.rs | 2 + ffi/capi/src/lib.rs | 2 + ffi/capi/src/timezone.rs | 21 ++ ffi/capi/src/timezone_mapper.rs | 205 ++++++++++++++++++ 37 files changed, 1489 insertions(+), 2 deletions(-) create mode 100644 ffi/capi/bindings/c/ICU4XTimeZoneIdMapper.h create mode 100644 ffi/capi/bindings/c/ICU4XTimeZoneIdMapperWithFastCanonicalization.h create mode 100644 ffi/capi/bindings/c/diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h create mode 100644 ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.h create mode 100644 ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.hpp create mode 100644 ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.h create mode 100644 ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.hpp create mode 100644 ffi/capi/bindings/cpp/diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h create mode 100644 ffi/capi/bindings/dart/TimeZoneIdMapper.g.dart create mode 100644 ffi/capi/bindings/dart/TimeZoneIdMapperWithFastCanonicalization.g.dart create mode 100644 ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.d.ts create mode 100644 ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.mjs create mode 100644 ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.d.ts create mode 100644 ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.mjs create mode 100644 ffi/capi/src/timezone_mapper.rs diff --git a/components/timezone/src/iana_ids.rs b/components/timezone/src/iana_ids.rs index a577d82455b..152d2000943 100644 --- a/components/timezone/src/iana_ids.rs +++ b/components/timezone/src/iana_ids.rs @@ -2,6 +2,8 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). +#![allow(deprecated)] // all APIS in here are deprecated + use crate::error::TimeZoneError; use crate::provider::names::*; use crate::TimeZoneBcp47Id; @@ -36,6 +38,7 @@ use icu_provider::prelude::*; /// ); /// ``` #[derive(Debug)] +#[deprecated(since = "1.5.0", note="use `TimeZoneIdMapper` instead")] pub struct IanaToBcp47Mapper { data: DataPayload, } @@ -91,6 +94,7 @@ impl IanaToBcp47Mapper { /// A borrowed wrapper around IANA-to-BCP47 time zone data, returned by /// [`IanaToBcp47Mapper::as_borrowed()`]. More efficient to query. #[derive(Debug)] +#[deprecated(since = "1.5.0", note="use `TimeZoneIdMapper` instead")] pub struct IanaToBcp47MapperBorrowed<'a> { data: &'a IanaToBcp47MapV1<'a>, } @@ -160,6 +164,7 @@ impl<'a> IanaToBcp47MapperBorrowed<'a> { /// assert_eq!(iana_id, Some("Asia/Kolkata")) /// ``` #[derive(Debug)] +#[deprecated(since = "1.5.0", note="use `TimeZoneIdMapper` instead")] pub struct IanaBcp47RoundTripMapper { data1: DataPayload, data2: DataPayload, @@ -229,6 +234,7 @@ impl IanaBcp47RoundTripMapper { /// A borrowed wrapper around IANA-BCP47 time zone data, returned by /// [`IanaBcp47RoundTripMapper::as_borrowed()`]. More efficient to query. #[derive(Debug)] +#[deprecated(since = "1.5.0", note="use `TimeZoneIdMapper` instead")] pub struct IanaBcp47RoundTripMapperBorrowed<'a> { data1: &'a IanaToBcp47MapV1<'a>, data2: &'a Bcp47ToIanaMapV1<'a>, diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index 05a867ce60b..c076e7edd9d 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -148,6 +148,12 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { .and_then(|trie_value| self.data.bcp47_ids.get(trie_value.index())) } + /// Same as [`Self::iana_to_bcp47()`] but works with potentially ill-formed UTF-8. + pub fn iana_bytes_to_bcp47(&self, iana_id: &[u8]) -> Option { + self.iana_lookup_quick(iana_id) + .and_then(|trie_value| self.data.bcp47_ids.get(trie_value.index())) + } + /// Normalizes the syntax of an IANA time zone ID. /// /// Also returns the BCP-47 time zone ID. @@ -298,7 +304,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// Queries the data for `iana_id` without recording the normalized string. /// This is a fast, no-alloc lookup. - fn iana_lookup_quick(&self, iana_id: &str) -> Option { + fn iana_lookup_quick(&self, iana_id: impl AsRef<[u8]>) -> Option { self.data.map.get(iana_id).map(IanaTrieValue) } diff --git a/components/timezone/src/lib.rs b/components/timezone/src/lib.rs index 41af4704697..39abb1f481e 100644 --- a/components/timezone/src/lib.rs +++ b/components/timezone/src/lib.rs @@ -134,6 +134,7 @@ mod time_zone; mod types; pub use error::TimeZoneError; +#[allow(deprecated)] pub use iana_ids::{ IanaBcp47RoundTripMapper, IanaBcp47RoundTripMapperBorrowed, IanaToBcp47Mapper, IanaToBcp47MapperBorrowed, diff --git a/ffi/capi/bindings/c/ICU4XCustomTimeZone.h b/ffi/capi/bindings/c/ICU4XCustomTimeZone.h index 422b7fa3827..c36fbd7d90b 100644 --- a/ffi/capi/bindings/c/ICU4XCustomTimeZone.h +++ b/ffi/capi/bindings/c/ICU4XCustomTimeZone.h @@ -19,6 +19,7 @@ typedef struct ICU4XCustomTimeZone ICU4XCustomTimeZone; #include "diplomat_result_int32_t_ICU4XError.h" #include "diplomat_result_bool_ICU4XError.h" #include "ICU4XIanaToBcp47Mapper.h" +#include "ICU4XTimeZoneIdMapper.h" #include "ICU4XMetazoneCalculator.h" #include "ICU4XIsoDateTime.h" #ifdef __cplusplus @@ -50,6 +51,8 @@ diplomat_result_void_ICU4XError ICU4XCustomTimeZone_try_set_time_zone_id(ICU4XCu diplomat_result_void_ICU4XError ICU4XCustomTimeZone_try_set_iana_time_zone_id(ICU4XCustomTimeZone* self, const ICU4XIanaToBcp47Mapper* mapper, const char* id_data, size_t id_len); +diplomat_result_void_ICU4XError ICU4XCustomTimeZone_try_set_iana_time_zone_id_2(ICU4XCustomTimeZone* self, const ICU4XTimeZoneIdMapper* mapper, const char* id_data, size_t id_len); + void ICU4XCustomTimeZone_clear_time_zone_id(ICU4XCustomTimeZone* self); diplomat_result_void_ICU4XError ICU4XCustomTimeZone_time_zone_id(const ICU4XCustomTimeZone* self, DiplomatWriteable* write); diff --git a/ffi/capi/bindings/c/ICU4XError.h b/ffi/capi/bindings/c/ICU4XError.h index 8836275ac58..b62d93a3ea9 100644 --- a/ffi/capi/bindings/c/ICU4XError.h +++ b/ffi/capi/bindings/c/ICU4XError.h @@ -14,6 +14,7 @@ typedef enum ICU4XError { ICU4XError_UnknownError = 0, ICU4XError_WriteableError = 1, ICU4XError_OutOfBoundsError = 2, + ICU4XError_Utf8Error = 3, ICU4XError_DataMissingDataKeyError = 256, ICU4XError_DataMissingVariantError = 257, ICU4XError_DataMissingLocaleError = 258, diff --git a/ffi/capi/bindings/c/ICU4XTimeZoneIdMapper.h b/ffi/capi/bindings/c/ICU4XTimeZoneIdMapper.h new file mode 100644 index 00000000000..134f5b6a9b8 --- /dev/null +++ b/ffi/capi/bindings/c/ICU4XTimeZoneIdMapper.h @@ -0,0 +1,40 @@ +#ifndef ICU4XTimeZoneIdMapper_H +#define ICU4XTimeZoneIdMapper_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +namespace capi { +#endif + +typedef struct ICU4XTimeZoneIdMapper ICU4XTimeZoneIdMapper; +#ifdef __cplusplus +} // namespace capi +#endif +#include "ICU4XDataProvider.h" +#include "diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h" +#include "diplomat_result_void_ICU4XError.h" +#ifdef __cplusplus +namespace capi { +extern "C" { +#endif + +diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError ICU4XTimeZoneIdMapper_create(const ICU4XDataProvider* provider); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapper_iana_to_bcp47(const ICU4XTimeZoneIdMapper* self, const char* value_data, size_t value_len, DiplomatWriteable* write); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapper_normalize_iana(const ICU4XTimeZoneIdMapper* self, const char* value_data, size_t value_len, DiplomatWriteable* write); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapper_canonicalize_iana(const ICU4XTimeZoneIdMapper* self, const char* value_data, size_t value_len, DiplomatWriteable* write); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapper_find_canonical_iana_from_bcp47(const ICU4XTimeZoneIdMapper* self, const char* value_data, size_t value_len, DiplomatWriteable* write); +void ICU4XTimeZoneIdMapper_destroy(ICU4XTimeZoneIdMapper* self); + +#ifdef __cplusplus +} // extern "C" +} // namespace capi +#endif +#endif diff --git a/ffi/capi/bindings/c/ICU4XTimeZoneIdMapperWithFastCanonicalization.h b/ffi/capi/bindings/c/ICU4XTimeZoneIdMapperWithFastCanonicalization.h new file mode 100644 index 00000000000..8d5ec644667 --- /dev/null +++ b/ffi/capi/bindings/c/ICU4XTimeZoneIdMapperWithFastCanonicalization.h @@ -0,0 +1,38 @@ +#ifndef ICU4XTimeZoneIdMapperWithFastCanonicalization_H +#define ICU4XTimeZoneIdMapperWithFastCanonicalization_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +namespace capi { +#endif + +typedef struct ICU4XTimeZoneIdMapperWithFastCanonicalization ICU4XTimeZoneIdMapperWithFastCanonicalization; +#ifdef __cplusplus +} // namespace capi +#endif +#include "ICU4XDataProvider.h" +#include "diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h" +#include "diplomat_result_void_ICU4XError.h" +#ifdef __cplusplus +namespace capi { +extern "C" { +#endif + +diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_create(const ICU4XDataProvider* provider); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); +void ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy(ICU4XTimeZoneIdMapperWithFastCanonicalization* self); + +#ifdef __cplusplus +} // extern "C" +} // namespace capi +#endif +#endif diff --git a/ffi/capi/bindings/c/diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h b/ffi/capi/bindings/c/diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h new file mode 100644 index 00000000000..79641973792 --- /dev/null +++ b/ffi/capi/bindings/c/diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h @@ -0,0 +1,26 @@ +#ifndef diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError_H +#define diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#include "ICU4XTimeZoneIdMapper.h" +#include "ICU4XError.h" +#ifdef __cplusplus +namespace capi { +extern "C" { +#endif +typedef struct diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError { + union { + ICU4XTimeZoneIdMapper* ok; + ICU4XError err; + }; + bool is_ok; +} diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError; +#ifdef __cplusplus +} // extern "C" +} // namespace capi +#endif +#endif diff --git a/ffi/capi/bindings/cpp/ICU4XCustomTimeZone.h b/ffi/capi/bindings/cpp/ICU4XCustomTimeZone.h index 422b7fa3827..c36fbd7d90b 100644 --- a/ffi/capi/bindings/cpp/ICU4XCustomTimeZone.h +++ b/ffi/capi/bindings/cpp/ICU4XCustomTimeZone.h @@ -19,6 +19,7 @@ typedef struct ICU4XCustomTimeZone ICU4XCustomTimeZone; #include "diplomat_result_int32_t_ICU4XError.h" #include "diplomat_result_bool_ICU4XError.h" #include "ICU4XIanaToBcp47Mapper.h" +#include "ICU4XTimeZoneIdMapper.h" #include "ICU4XMetazoneCalculator.h" #include "ICU4XIsoDateTime.h" #ifdef __cplusplus @@ -50,6 +51,8 @@ diplomat_result_void_ICU4XError ICU4XCustomTimeZone_try_set_time_zone_id(ICU4XCu diplomat_result_void_ICU4XError ICU4XCustomTimeZone_try_set_iana_time_zone_id(ICU4XCustomTimeZone* self, const ICU4XIanaToBcp47Mapper* mapper, const char* id_data, size_t id_len); +diplomat_result_void_ICU4XError ICU4XCustomTimeZone_try_set_iana_time_zone_id_2(ICU4XCustomTimeZone* self, const ICU4XTimeZoneIdMapper* mapper, const char* id_data, size_t id_len); + void ICU4XCustomTimeZone_clear_time_zone_id(ICU4XCustomTimeZone* self); diplomat_result_void_ICU4XError ICU4XCustomTimeZone_time_zone_id(const ICU4XCustomTimeZone* self, DiplomatWriteable* write); diff --git a/ffi/capi/bindings/cpp/ICU4XCustomTimeZone.hpp b/ffi/capi/bindings/cpp/ICU4XCustomTimeZone.hpp index 7b3b375da1e..1c9eb452ca6 100644 --- a/ffi/capi/bindings/cpp/ICU4XCustomTimeZone.hpp +++ b/ffi/capi/bindings/cpp/ICU4XCustomTimeZone.hpp @@ -14,6 +14,7 @@ class ICU4XCustomTimeZone; #include "ICU4XError.hpp" class ICU4XIanaToBcp47Mapper; +class ICU4XTimeZoneIdMapper; class ICU4XMetazoneCalculator; class ICU4XIsoDateTime; @@ -141,6 +142,14 @@ class ICU4XCustomTimeZone { */ diplomat::result try_set_iana_time_zone_id(const ICU4XIanaToBcp47Mapper& mapper, const std::string_view id); + /** + * Sets the `time_zone_id` field from an IANA string by looking up + * the corresponding BCP-47 string. + * + * Errors if the string is not a valid BCP-47 time zone ID. + */ + diplomat::result try_set_iana_time_zone_id_2(const ICU4XTimeZoneIdMapper& mapper, const std::string_view id); + /** * Clears the `time_zone_id` field. * @@ -315,6 +324,7 @@ class ICU4XCustomTimeZone { }; #include "ICU4XIanaToBcp47Mapper.hpp" +#include "ICU4XTimeZoneIdMapper.hpp" #include "ICU4XMetazoneCalculator.hpp" #include "ICU4XIsoDateTime.hpp" @@ -417,6 +427,16 @@ inline diplomat::result ICU4XCustomTimeZone::try_set } return diplomat_result_out_value; } +inline diplomat::result ICU4XCustomTimeZone::try_set_iana_time_zone_id_2(const ICU4XTimeZoneIdMapper& mapper, const std::string_view id) { + auto diplomat_result_raw_out_value = capi::ICU4XCustomTimeZone_try_set_iana_time_zone_id_2(this->inner.get(), mapper.AsFFI(), id.data(), id.size()); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value; +} inline void ICU4XCustomTimeZone::clear_time_zone_id() { capi::ICU4XCustomTimeZone_clear_time_zone_id(this->inner.get()); } diff --git a/ffi/capi/bindings/cpp/ICU4XError.h b/ffi/capi/bindings/cpp/ICU4XError.h index 8836275ac58..b62d93a3ea9 100644 --- a/ffi/capi/bindings/cpp/ICU4XError.h +++ b/ffi/capi/bindings/cpp/ICU4XError.h @@ -14,6 +14,7 @@ typedef enum ICU4XError { ICU4XError_UnknownError = 0, ICU4XError_WriteableError = 1, ICU4XError_OutOfBoundsError = 2, + ICU4XError_Utf8Error = 3, ICU4XError_DataMissingDataKeyError = 256, ICU4XError_DataMissingVariantError = 257, ICU4XError_DataMissingLocaleError = 258, diff --git a/ffi/capi/bindings/cpp/ICU4XError.hpp b/ffi/capi/bindings/cpp/ICU4XError.hpp index 1a306ca2b55..ed5bacf18eb 100644 --- a/ffi/capi/bindings/cpp/ICU4XError.hpp +++ b/ffi/capi/bindings/cpp/ICU4XError.hpp @@ -34,7 +34,16 @@ enum struct ICU4XError { * Most APIs that return a string may return this error */ WriteableError = 1, + + /** + * Some input was out of bounds + */ OutOfBoundsError = 2, + + /** + * Input expected to be UTF-8 was ill-formed + */ + Utf8Error = 3, DataMissingDataKeyError = 256, DataMissingVariantError = 257, DataMissingLocaleError = 258, diff --git a/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.h b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.h new file mode 100644 index 00000000000..134f5b6a9b8 --- /dev/null +++ b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.h @@ -0,0 +1,40 @@ +#ifndef ICU4XTimeZoneIdMapper_H +#define ICU4XTimeZoneIdMapper_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +namespace capi { +#endif + +typedef struct ICU4XTimeZoneIdMapper ICU4XTimeZoneIdMapper; +#ifdef __cplusplus +} // namespace capi +#endif +#include "ICU4XDataProvider.h" +#include "diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h" +#include "diplomat_result_void_ICU4XError.h" +#ifdef __cplusplus +namespace capi { +extern "C" { +#endif + +diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError ICU4XTimeZoneIdMapper_create(const ICU4XDataProvider* provider); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapper_iana_to_bcp47(const ICU4XTimeZoneIdMapper* self, const char* value_data, size_t value_len, DiplomatWriteable* write); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapper_normalize_iana(const ICU4XTimeZoneIdMapper* self, const char* value_data, size_t value_len, DiplomatWriteable* write); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapper_canonicalize_iana(const ICU4XTimeZoneIdMapper* self, const char* value_data, size_t value_len, DiplomatWriteable* write); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapper_find_canonical_iana_from_bcp47(const ICU4XTimeZoneIdMapper* self, const char* value_data, size_t value_len, DiplomatWriteable* write); +void ICU4XTimeZoneIdMapper_destroy(ICU4XTimeZoneIdMapper* self); + +#ifdef __cplusplus +} // extern "C" +} // namespace capi +#endif +#endif diff --git a/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.hpp b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.hpp new file mode 100644 index 00000000000..b4b7bce0b28 --- /dev/null +++ b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.hpp @@ -0,0 +1,196 @@ +#ifndef ICU4XTimeZoneIdMapper_HPP +#define ICU4XTimeZoneIdMapper_HPP +#include +#include +#include +#include +#include +#include +#include +#include "diplomat_runtime.hpp" + +#include "ICU4XTimeZoneIdMapper.h" + +class ICU4XDataProvider; +class ICU4XTimeZoneIdMapper; +#include "ICU4XError.hpp" + +/** + * A destruction policy for using ICU4XTimeZoneIdMapper with std::unique_ptr. + */ +struct ICU4XTimeZoneIdMapperDeleter { + void operator()(capi::ICU4XTimeZoneIdMapper* l) const noexcept { + capi::ICU4XTimeZoneIdMapper_destroy(l); + } +}; + +/** + * A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. + * + * This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. + * It also supports normalizing and canonicalizing the IANA strings. + * + * See the [Rust documentation for `TimeZoneIdMapper`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapper.html) for more information. + */ +class ICU4XTimeZoneIdMapper { + public: + + /** + * See the [Rust documentation for `new`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapper.html#method.new) for more information. + */ + static diplomat::result create(const ICU4XDataProvider& provider); + + /** + * See the [Rust documentation for `iana_to_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.iana_to_bcp47) for more information. + */ + template diplomat::result iana_to_bcp47_to_writeable(const std::string_view value, W& write) const; + + /** + * See the [Rust documentation for `iana_to_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.iana_to_bcp47) for more information. + */ + diplomat::result iana_to_bcp47(const std::string_view value) const; + + /** + * See the [Rust documentation for `normalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana) for more information. + */ + template diplomat::result normalize_iana_to_writeable(const std::string_view value, W& write) const; + + /** + * See the [Rust documentation for `normalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana) for more information. + */ + diplomat::result normalize_iana(const std::string_view value) const; + + /** + * See the [Rust documentation for `normalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana) for more information. + */ + template diplomat::result canonicalize_iana_to_writeable(const std::string_view value, W& write) const; + + /** + * See the [Rust documentation for `normalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana) for more information. + */ + diplomat::result canonicalize_iana(const std::string_view value) const; + + /** + * See the [Rust documentation for `find_canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.find_canonical_iana_from_bcp47) for more information. + */ + template diplomat::result find_canonical_iana_from_bcp47_to_writeable(const std::string_view value, W& write) const; + + /** + * See the [Rust documentation for `find_canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.find_canonical_iana_from_bcp47) for more information. + */ + diplomat::result find_canonical_iana_from_bcp47(const std::string_view value) const; + inline const capi::ICU4XTimeZoneIdMapper* AsFFI() const { return this->inner.get(); } + inline capi::ICU4XTimeZoneIdMapper* AsFFIMut() { return this->inner.get(); } + inline explicit ICU4XTimeZoneIdMapper(capi::ICU4XTimeZoneIdMapper* i) : inner(i) {} + ICU4XTimeZoneIdMapper() = default; + ICU4XTimeZoneIdMapper(ICU4XTimeZoneIdMapper&&) noexcept = default; + ICU4XTimeZoneIdMapper& operator=(ICU4XTimeZoneIdMapper&& other) noexcept = default; + private: + std::unique_ptr inner; +}; + +#include "ICU4XDataProvider.hpp" + +inline diplomat::result ICU4XTimeZoneIdMapper::create(const ICU4XDataProvider& provider) { + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapper_create(provider.AsFFI()); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(ICU4XTimeZoneIdMapper(diplomat_result_raw_out_value.ok)); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value; +} +template inline diplomat::result ICU4XTimeZoneIdMapper::iana_to_bcp47_to_writeable(const std::string_view value, W& write) const { + capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapper_iana_to_bcp47(this->inner.get(), value.data(), value.size(), &write_writer); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value; +} +inline diplomat::result ICU4XTimeZoneIdMapper::iana_to_bcp47(const std::string_view value) const { + std::string diplomat_writeable_string; + capi::DiplomatWriteable diplomat_writeable_out = diplomat::WriteableFromString(diplomat_writeable_string); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapper_iana_to_bcp47(this->inner.get(), value.data(), value.size(), &diplomat_writeable_out); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value.replace_ok(std::move(diplomat_writeable_string)); +} +template inline diplomat::result ICU4XTimeZoneIdMapper::normalize_iana_to_writeable(const std::string_view value, W& write) const { + capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapper_normalize_iana(this->inner.get(), value.data(), value.size(), &write_writer); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value; +} +inline diplomat::result ICU4XTimeZoneIdMapper::normalize_iana(const std::string_view value) const { + std::string diplomat_writeable_string; + capi::DiplomatWriteable diplomat_writeable_out = diplomat::WriteableFromString(diplomat_writeable_string); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapper_normalize_iana(this->inner.get(), value.data(), value.size(), &diplomat_writeable_out); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value.replace_ok(std::move(diplomat_writeable_string)); +} +template inline diplomat::result ICU4XTimeZoneIdMapper::canonicalize_iana_to_writeable(const std::string_view value, W& write) const { + capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapper_canonicalize_iana(this->inner.get(), value.data(), value.size(), &write_writer); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value; +} +inline diplomat::result ICU4XTimeZoneIdMapper::canonicalize_iana(const std::string_view value) const { + std::string diplomat_writeable_string; + capi::DiplomatWriteable diplomat_writeable_out = diplomat::WriteableFromString(diplomat_writeable_string); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapper_canonicalize_iana(this->inner.get(), value.data(), value.size(), &diplomat_writeable_out); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value.replace_ok(std::move(diplomat_writeable_string)); +} +template inline diplomat::result ICU4XTimeZoneIdMapper::find_canonical_iana_from_bcp47_to_writeable(const std::string_view value, W& write) const { + capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapper_find_canonical_iana_from_bcp47(this->inner.get(), value.data(), value.size(), &write_writer); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value; +} +inline diplomat::result ICU4XTimeZoneIdMapper::find_canonical_iana_from_bcp47(const std::string_view value) const { + std::string diplomat_writeable_string; + capi::DiplomatWriteable diplomat_writeable_out = diplomat::WriteableFromString(diplomat_writeable_string); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapper_find_canonical_iana_from_bcp47(this->inner.get(), value.data(), value.size(), &diplomat_writeable_out); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value.replace_ok(std::move(diplomat_writeable_string)); +} +#endif diff --git a/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.h b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.h new file mode 100644 index 00000000000..8d5ec644667 --- /dev/null +++ b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.h @@ -0,0 +1,38 @@ +#ifndef ICU4XTimeZoneIdMapperWithFastCanonicalization_H +#define ICU4XTimeZoneIdMapperWithFastCanonicalization_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +namespace capi { +#endif + +typedef struct ICU4XTimeZoneIdMapperWithFastCanonicalization ICU4XTimeZoneIdMapperWithFastCanonicalization; +#ifdef __cplusplus +} // namespace capi +#endif +#include "ICU4XDataProvider.h" +#include "diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h" +#include "diplomat_result_void_ICU4XError.h" +#ifdef __cplusplus +namespace capi { +extern "C" { +#endif + +diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_create(const ICU4XDataProvider* provider); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); + +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); +void ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy(ICU4XTimeZoneIdMapperWithFastCanonicalization* self); + +#ifdef __cplusplus +} // extern "C" +} // namespace capi +#endif +#endif diff --git a/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.hpp b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.hpp new file mode 100644 index 00000000000..1e58e187e5e --- /dev/null +++ b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.hpp @@ -0,0 +1,164 @@ +#ifndef ICU4XTimeZoneIdMapperWithFastCanonicalization_HPP +#define ICU4XTimeZoneIdMapperWithFastCanonicalization_HPP +#include +#include +#include +#include +#include +#include +#include +#include "diplomat_runtime.hpp" + +#include "ICU4XTimeZoneIdMapperWithFastCanonicalization.h" + +class ICU4XDataProvider; +class ICU4XTimeZoneIdMapper; +#include "ICU4XError.hpp" + +/** + * A destruction policy for using ICU4XTimeZoneIdMapperWithFastCanonicalization with std::unique_ptr. + */ +struct ICU4XTimeZoneIdMapperWithFastCanonicalizationDeleter { + void operator()(capi::ICU4XTimeZoneIdMapperWithFastCanonicalization* l) const noexcept { + capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy(l); + } +}; + +/** + * A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. + * + * This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. + * It also supports normalizing and canonicalizing the IANA strings. + * + * See the [Rust documentation for `TimeZoneIdMapperWithFastCanonicalization`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalization.html) for more information. + */ +class ICU4XTimeZoneIdMapperWithFastCanonicalization { + public: + + /** + * See the [Rust documentation for `new`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalization.html#method.new) for more information. + */ + static diplomat::result create(const ICU4XDataProvider& provider); + + /** + * See the [Rust documentation for `iana_to_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.iana_to_bcp47) for more information. + */ + template diplomat::result iana_to_bcp47_to_writeable(const std::string_view value, W& write) const; + + /** + * See the [Rust documentation for `iana_to_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.iana_to_bcp47) for more information. + */ + diplomat::result iana_to_bcp47(const std::string_view value) const; + + /** + * See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonicalize_iana) for more information. + */ + template diplomat::result canonicalize_iana_to_writeable(const std::string_view value, W& write) const; + + /** + * See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonicalize_iana) for more information. + */ + diplomat::result canonicalize_iana(const std::string_view value) const; + + /** + * See the [Rust documentation for `canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonical_iana_from_bcp47) for more information. + */ + template diplomat::result find_canonical_iana_from_bcp47_to_writeable(const std::string_view value, W& write) const; + + /** + * See the [Rust documentation for `canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonical_iana_from_bcp47) for more information. + */ + diplomat::result find_canonical_iana_from_bcp47(const std::string_view value) const; + inline const capi::ICU4XTimeZoneIdMapperWithFastCanonicalization* AsFFI() const { return this->inner.get(); } + inline capi::ICU4XTimeZoneIdMapperWithFastCanonicalization* AsFFIMut() { return this->inner.get(); } + inline explicit ICU4XTimeZoneIdMapperWithFastCanonicalization(capi::ICU4XTimeZoneIdMapperWithFastCanonicalization* i) : inner(i) {} + ICU4XTimeZoneIdMapperWithFastCanonicalization() = default; + ICU4XTimeZoneIdMapperWithFastCanonicalization(ICU4XTimeZoneIdMapperWithFastCanonicalization&&) noexcept = default; + ICU4XTimeZoneIdMapperWithFastCanonicalization& operator=(ICU4XTimeZoneIdMapperWithFastCanonicalization&& other) noexcept = default; + private: + std::unique_ptr inner; +}; + +#include "ICU4XDataProvider.hpp" +#include "ICU4XTimeZoneIdMapper.hpp" + +inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::create(const ICU4XDataProvider& provider) { + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_create(provider.AsFFI()); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(ICU4XTimeZoneIdMapper(diplomat_result_raw_out_value.ok)); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value; +} +template inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::iana_to_bcp47_to_writeable(const std::string_view value, W& write) const { + capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(this->inner.get(), value.data(), value.size(), &write_writer); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value; +} +inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::iana_to_bcp47(const std::string_view value) const { + std::string diplomat_writeable_string; + capi::DiplomatWriteable diplomat_writeable_out = diplomat::WriteableFromString(diplomat_writeable_string); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(this->inner.get(), value.data(), value.size(), &diplomat_writeable_out); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value.replace_ok(std::move(diplomat_writeable_string)); +} +template inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::canonicalize_iana_to_writeable(const std::string_view value, W& write) const { + capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(this->inner.get(), value.data(), value.size(), &write_writer); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value; +} +inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::canonicalize_iana(const std::string_view value) const { + std::string diplomat_writeable_string; + capi::DiplomatWriteable diplomat_writeable_out = diplomat::WriteableFromString(diplomat_writeable_string); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(this->inner.get(), value.data(), value.size(), &diplomat_writeable_out); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value.replace_ok(std::move(diplomat_writeable_string)); +} +template inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::find_canonical_iana_from_bcp47_to_writeable(const std::string_view value, W& write) const { + capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(this->inner.get(), value.data(), value.size(), &write_writer); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value; +} +inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::find_canonical_iana_from_bcp47(const std::string_view value) const { + std::string diplomat_writeable_string; + capi::DiplomatWriteable diplomat_writeable_out = diplomat::WriteableFromString(diplomat_writeable_string); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(this->inner.get(), value.data(), value.size(), &diplomat_writeable_out); + diplomat::result diplomat_result_out_value; + if (diplomat_result_raw_out_value.is_ok) { + diplomat_result_out_value = diplomat::Ok(std::monostate()); + } else { + diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); + } + return diplomat_result_out_value.replace_ok(std::move(diplomat_writeable_string)); +} +#endif diff --git a/ffi/capi/bindings/cpp/diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h b/ffi/capi/bindings/cpp/diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h new file mode 100644 index 00000000000..79641973792 --- /dev/null +++ b/ffi/capi/bindings/cpp/diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h @@ -0,0 +1,26 @@ +#ifndef diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError_H +#define diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#include "ICU4XTimeZoneIdMapper.h" +#include "ICU4XError.h" +#ifdef __cplusplus +namespace capi { +extern "C" { +#endif +typedef struct diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError { + union { + ICU4XTimeZoneIdMapper* ok; + ICU4XError err; + }; + bool is_ok; +} diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError; +#ifdef __cplusplus +} // extern "C" +} // namespace capi +#endif +#endif diff --git a/ffi/capi/bindings/dart/CustomTimeZone.g.dart b/ffi/capi/bindings/dart/CustomTimeZone.g.dart index 21a7f4b2ae6..ed9718a93b1 100644 --- a/ffi/capi/bindings/dart/CustomTimeZone.g.dart +++ b/ffi/capi/bindings/dart/CustomTimeZone.g.dart @@ -196,6 +196,23 @@ final class CustomTimeZone implements ffi.Finalizable { } + /// Sets the `time_zone_id` field from an IANA string by looking up + /// the corresponding BCP-47 string. + /// + /// Errors if the string is not a valid BCP-47 time zone ID. + /// + /// Throws [Error] on failure. + void trySetIanaTimeZoneId2(TimeZoneIdMapper mapper, String id) { + final temp = ffi2.Arena(); + final idView = id.utf8View; + final result = _ICU4XCustomTimeZone_try_set_iana_time_zone_id_2(_ffi, mapper._ffi, idView.allocIn(temp), idView.length); + temp.releaseAll(); + if (!result.isOk) { + throw Error.values.firstWhere((v) => v._ffi == result.union.err); + } + + } + /// Clears the `time_zone_id` field. /// /// See the [Rust documentation for `time_zone_id`](https://docs.rs/icu/latest/icu/timezone/struct.CustomTimeZone.html#structfield.time_zone_id) for more information. @@ -444,6 +461,11 @@ external _ResultVoidInt32 _ICU4XCustomTimeZone_try_set_time_zone_id(ffi.Pointer< // ignore: non_constant_identifier_names external _ResultVoidInt32 _ICU4XCustomTimeZone_try_set_iana_time_zone_id(ffi.Pointer self, ffi.Pointer mapper, ffi.Pointer idData, int idLength); +@meta.ResourceIdentifier('ICU4XCustomTimeZone_try_set_iana_time_zone_id_2') +@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Pointer, ffi.Size)>(isLeaf: true, symbol: 'ICU4XCustomTimeZone_try_set_iana_time_zone_id_2') +// ignore: non_constant_identifier_names +external _ResultVoidInt32 _ICU4XCustomTimeZone_try_set_iana_time_zone_id_2(ffi.Pointer self, ffi.Pointer mapper, ffi.Pointer idData, int idLength); + @meta.ResourceIdentifier('ICU4XCustomTimeZone_clear_time_zone_id') @ffi.Native)>(isLeaf: true, symbol: 'ICU4XCustomTimeZone_clear_time_zone_id') // ignore: non_constant_identifier_names diff --git a/ffi/capi/bindings/dart/Error.g.dart b/ffi/capi/bindings/dart/Error.g.dart index 8f18a7e6a27..5499cb73de2 100644 --- a/ffi/capi/bindings/dart/Error.g.dart +++ b/ffi/capi/bindings/dart/Error.g.dart @@ -17,8 +17,12 @@ enum Error { /// Most APIs that return a string may return this error writeableError, + /// Some input was out of bounds outOfBoundsError, + /// Input expected to be UTF-8 was ill-formed + utf8Error, + dataMissingDataKeyError, dataMissingVariantError, @@ -134,6 +138,8 @@ enum Error { return 1; case outOfBoundsError: return 2; + case utf8Error: + return 3; case dataMissingDataKeyError: return 256; case dataMissingVariantError: diff --git a/ffi/capi/bindings/dart/TimeZoneIdMapper.g.dart b/ffi/capi/bindings/dart/TimeZoneIdMapper.g.dart new file mode 100644 index 00000000000..0d7b5e8c14c --- /dev/null +++ b/ffi/capi/bindings/dart/TimeZoneIdMapper.g.dart @@ -0,0 +1,130 @@ +// generated by diplomat-tool + +part of 'lib.g.dart'; + +/// A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. +/// +/// This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. +/// It also supports normalizing and canonicalizing the IANA strings. +/// +/// See the [Rust documentation for `TimeZoneIdMapper`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapper.html) for more information. +final class TimeZoneIdMapper implements ffi.Finalizable { + final ffi.Pointer _ffi; + + // These are "used" in the sense that they keep dependencies alive + // ignore: unused_field + final core.List _selfEdge; + + // This takes in a list of lifetime edges (including for &self borrows) + // corresponding to data this may borrow from. These should be flat arrays containing + // references to objects, and this object will hold on to them to keep them alive and + // maintain borrow validity. + TimeZoneIdMapper._fromFfi(this._ffi, this._selfEdge) { + if (_selfEdge.isEmpty) { + _finalizer.attach(this, _ffi.cast()); + } + } + + static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_ICU4XTimeZoneIdMapper_destroy)); + + /// See the [Rust documentation for `new`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapper.html#method.new) for more information. + /// + /// Throws [Error] on failure. + factory TimeZoneIdMapper(DataProvider provider) { + final result = _ICU4XTimeZoneIdMapper_create(provider._ffi); + if (!result.isOk) { + throw Error.values.firstWhere((v) => v._ffi == result.union.err); + } + return TimeZoneIdMapper._fromFfi(result.union.ok, []); + } + + /// See the [Rust documentation for `iana_to_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.iana_to_bcp47) for more information. + /// + /// Throws [Error] on failure. + String ianaToBcp47(String value) { + final temp = ffi2.Arena(); + final valueView = value.utf8View; + final writeable = _Writeable(); + final result = _ICU4XTimeZoneIdMapper_iana_to_bcp47(_ffi, valueView.allocIn(temp), valueView.length, writeable._ffi); + temp.releaseAll(); + if (!result.isOk) { + throw Error.values.firstWhere((v) => v._ffi == result.union.err); + } + return writeable.finalize(); + } + + /// See the [Rust documentation for `normalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana) for more information. + /// + /// Throws [Error] on failure. + String normalizeIana(String value) { + final temp = ffi2.Arena(); + final valueView = value.utf8View; + final writeable = _Writeable(); + final result = _ICU4XTimeZoneIdMapper_normalize_iana(_ffi, valueView.allocIn(temp), valueView.length, writeable._ffi); + temp.releaseAll(); + if (!result.isOk) { + throw Error.values.firstWhere((v) => v._ffi == result.union.err); + } + return writeable.finalize(); + } + + /// See the [Rust documentation for `normalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana) for more information. + /// + /// Throws [Error] on failure. + String canonicalizeIana(String value) { + final temp = ffi2.Arena(); + final valueView = value.utf8View; + final writeable = _Writeable(); + final result = _ICU4XTimeZoneIdMapper_canonicalize_iana(_ffi, valueView.allocIn(temp), valueView.length, writeable._ffi); + temp.releaseAll(); + if (!result.isOk) { + throw Error.values.firstWhere((v) => v._ffi == result.union.err); + } + return writeable.finalize(); + } + + /// See the [Rust documentation for `find_canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.find_canonical_iana_from_bcp47) for more information. + /// + /// Throws [Error] on failure. + String findCanonicalIanaFromBcp47(String value) { + final temp = ffi2.Arena(); + final valueView = value.utf8View; + final writeable = _Writeable(); + final result = _ICU4XTimeZoneIdMapper_find_canonical_iana_from_bcp47(_ffi, valueView.allocIn(temp), valueView.length, writeable._ffi); + temp.releaseAll(); + if (!result.isOk) { + throw Error.values.firstWhere((v) => v._ffi == result.union.err); + } + return writeable.finalize(); + } +} + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapper_destroy') +@ffi.Native)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapper_destroy') +// ignore: non_constant_identifier_names +external void _ICU4XTimeZoneIdMapper_destroy(ffi.Pointer self); + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapper_create') +@ffi.Native<_ResultOpaqueInt32 Function(ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapper_create') +// ignore: non_constant_identifier_names +external _ResultOpaqueInt32 _ICU4XTimeZoneIdMapper_create(ffi.Pointer provider); + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapper_iana_to_bcp47') +@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapper_iana_to_bcp47') +// ignore: non_constant_identifier_names +external _ResultVoidInt32 _ICU4XTimeZoneIdMapper_iana_to_bcp47(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapper_normalize_iana') +@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapper_normalize_iana') +// ignore: non_constant_identifier_names +external _ResultVoidInt32 _ICU4XTimeZoneIdMapper_normalize_iana(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapper_canonicalize_iana') +@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapper_canonicalize_iana') +// ignore: non_constant_identifier_names +external _ResultVoidInt32 _ICU4XTimeZoneIdMapper_canonicalize_iana(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapper_find_canonical_iana_from_bcp47') +@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapper_find_canonical_iana_from_bcp47') +// ignore: non_constant_identifier_names +external _ResultVoidInt32 _ICU4XTimeZoneIdMapper_find_canonical_iana_from_bcp47(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); diff --git a/ffi/capi/bindings/dart/TimeZoneIdMapperWithFastCanonicalization.g.dart b/ffi/capi/bindings/dart/TimeZoneIdMapperWithFastCanonicalization.g.dart new file mode 100644 index 00000000000..cc08a4c2c88 --- /dev/null +++ b/ffi/capi/bindings/dart/TimeZoneIdMapperWithFastCanonicalization.g.dart @@ -0,0 +1,110 @@ +// generated by diplomat-tool + +part of 'lib.g.dart'; + +/// A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. +/// +/// This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. +/// It also supports normalizing and canonicalizing the IANA strings. +/// +/// See the [Rust documentation for `TimeZoneIdMapperWithFastCanonicalization`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalization.html) for more information. +final class TimeZoneIdMapperWithFastCanonicalization implements ffi.Finalizable { + final ffi.Pointer _ffi; + + // These are "used" in the sense that they keep dependencies alive + // ignore: unused_field + final core.List _selfEdge; + + // This takes in a list of lifetime edges (including for &self borrows) + // corresponding to data this may borrow from. These should be flat arrays containing + // references to objects, and this object will hold on to them to keep them alive and + // maintain borrow validity. + TimeZoneIdMapperWithFastCanonicalization._fromFfi(this._ffi, this._selfEdge) { + if (_selfEdge.isEmpty) { + _finalizer.attach(this, _ffi.cast()); + } + } + + static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy)); + + /// See the [Rust documentation for `new`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalization.html#method.new) for more information. + /// + /// Throws [Error] on failure. + static TimeZoneIdMapper create(DataProvider provider) { + final result = _ICU4XTimeZoneIdMapperWithFastCanonicalization_create(provider._ffi); + if (!result.isOk) { + throw Error.values.firstWhere((v) => v._ffi == result.union.err); + } + return TimeZoneIdMapper._fromFfi(result.union.ok, []); + } + + /// See the [Rust documentation for `iana_to_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.iana_to_bcp47) for more information. + /// + /// Throws [Error] on failure. + String ianaToBcp47(String value) { + final temp = ffi2.Arena(); + final valueView = value.utf8View; + final writeable = _Writeable(); + final result = _ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(_ffi, valueView.allocIn(temp), valueView.length, writeable._ffi); + temp.releaseAll(); + if (!result.isOk) { + throw Error.values.firstWhere((v) => v._ffi == result.union.err); + } + return writeable.finalize(); + } + + /// See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonicalize_iana) for more information. + /// + /// Throws [Error] on failure. + String canonicalizeIana(String value) { + final temp = ffi2.Arena(); + final valueView = value.utf8View; + final writeable = _Writeable(); + final result = _ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(_ffi, valueView.allocIn(temp), valueView.length, writeable._ffi); + temp.releaseAll(); + if (!result.isOk) { + throw Error.values.firstWhere((v) => v._ffi == result.union.err); + } + return writeable.finalize(); + } + + /// See the [Rust documentation for `canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonical_iana_from_bcp47) for more information. + /// + /// Throws [Error] on failure. + String findCanonicalIanaFromBcp47(String value) { + final temp = ffi2.Arena(); + final valueView = value.utf8View; + final writeable = _Writeable(); + final result = _ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(_ffi, valueView.allocIn(temp), valueView.length, writeable._ffi); + temp.releaseAll(); + if (!result.isOk) { + throw Error.values.firstWhere((v) => v._ffi == result.union.err); + } + return writeable.finalize(); + } +} + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy') +@ffi.Native)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy') +// ignore: non_constant_identifier_names +external void _ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy(ffi.Pointer self); + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapperWithFastCanonicalization_create') +@ffi.Native<_ResultOpaqueInt32 Function(ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapperWithFastCanonicalization_create') +// ignore: non_constant_identifier_names +external _ResultOpaqueInt32 _ICU4XTimeZoneIdMapperWithFastCanonicalization_create(ffi.Pointer provider); + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47') +@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47') +// ignore: non_constant_identifier_names +external _ResultVoidInt32 _ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana') +@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana') +// ignore: non_constant_identifier_names +external _ResultVoidInt32 _ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); + +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47') +@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47') +// ignore: non_constant_identifier_names +external _ResultVoidInt32 _ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); diff --git a/ffi/capi/bindings/dart/lib.g.dart b/ffi/capi/bindings/dart/lib.g.dart index 180a706098f..d3e22651959 100644 --- a/ffi/capi/bindings/dart/lib.g.dart +++ b/ffi/capi/bindings/dart/lib.g.dart @@ -118,6 +118,8 @@ part 'Time.g.dart'; part 'TimeFormatter.g.dart'; part 'TimeLength.g.dart'; part 'TimeZoneFormatter.g.dart'; +part 'TimeZoneIdMapper.g.dart'; +part 'TimeZoneIdMapperWithFastCanonicalization.g.dart'; part 'TitlecaseMapper.g.dart'; part 'TitlecaseOptions.g.dart'; part 'TrailingCase.g.dart'; diff --git a/ffi/capi/bindings/js/ICU4XCustomTimeZone.d.ts b/ffi/capi/bindings/js/ICU4XCustomTimeZone.d.ts index 290f7881ad3..bd1c61c7230 100644 --- a/ffi/capi/bindings/js/ICU4XCustomTimeZone.d.ts +++ b/ffi/capi/bindings/js/ICU4XCustomTimeZone.d.ts @@ -4,6 +4,7 @@ import { ICU4XError } from "./ICU4XError"; import { ICU4XIanaToBcp47Mapper } from "./ICU4XIanaToBcp47Mapper"; import { ICU4XIsoDateTime } from "./ICU4XIsoDateTime"; import { ICU4XMetazoneCalculator } from "./ICU4XMetazoneCalculator"; +import { ICU4XTimeZoneIdMapper } from "./ICU4XTimeZoneIdMapper"; /** @@ -140,6 +141,15 @@ export class ICU4XCustomTimeZone { */ try_set_iana_time_zone_id(mapper: ICU4XIanaToBcp47Mapper, id: string): void | never; + /** + + * Sets the `time_zone_id` field from an IANA string by looking up the corresponding BCP-47 string. + + * Errors if the string is not a valid BCP-47 time zone ID. + * @throws {@link FFIError}<{@link ICU4XError}> + */ + try_set_iana_time_zone_id_2(mapper: ICU4XTimeZoneIdMapper, id: string): void | never; + /** * Clears the `time_zone_id` field. diff --git a/ffi/capi/bindings/js/ICU4XCustomTimeZone.mjs b/ffi/capi/bindings/js/ICU4XCustomTimeZone.mjs index 154ab95a549..747011e02af 100644 --- a/ffi/capi/bindings/js/ICU4XCustomTimeZone.mjs +++ b/ffi/capi/bindings/js/ICU4XCustomTimeZone.mjs @@ -190,6 +190,26 @@ export class ICU4XCustomTimeZone { return diplomat_out; } + try_set_iana_time_zone_id_2(arg_mapper, arg_id) { + const buf_arg_id = diplomatRuntime.DiplomatBuf.str8(wasm, arg_id); + const diplomat_out = (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); + wasm.ICU4XCustomTimeZone_try_set_iana_time_zone_id_2(diplomat_receive_buffer, this.underlying, arg_mapper.underlying, buf_arg_id.ptr, buf_arg_id.size); + const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); + if (is_ok) { + const ok_value = {}; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + return ok_value; + } else { + const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + throw new diplomatRuntime.FFIError(throw_value); + } + })(); + buf_arg_id.free(); + return diplomat_out; + } + clear_time_zone_id() { wasm.ICU4XCustomTimeZone_clear_time_zone_id(this.underlying); } diff --git a/ffi/capi/bindings/js/ICU4XError.d.ts b/ffi/capi/bindings/js/ICU4XError.d.ts index 882c7c6c551..852913f3b23 100644 --- a/ffi/capi/bindings/js/ICU4XError.d.ts +++ b/ffi/capi/bindings/js/ICU4XError.d.ts @@ -19,8 +19,15 @@ export enum ICU4XError { */ WriteableError = 'WriteableError', /** + + * Some input was out of bounds */ OutOfBoundsError = 'OutOfBoundsError', + /** + + * Input expected to be UTF-8 was ill-formed + */ + Utf8Error = 'Utf8Error', /** */ DataMissingDataKeyError = 'DataMissingDataKeyError', diff --git a/ffi/capi/bindings/js/ICU4XError.mjs b/ffi/capi/bindings/js/ICU4XError.mjs index 31c5bb0729f..42e3a20707b 100644 --- a/ffi/capi/bindings/js/ICU4XError.mjs +++ b/ffi/capi/bindings/js/ICU4XError.mjs @@ -5,6 +5,7 @@ export const ICU4XError_js_to_rust = { "UnknownError": 0, "WriteableError": 1, "OutOfBoundsError": 2, + "Utf8Error": 3, "DataMissingDataKeyError": 256, "DataMissingVariantError": 257, "DataMissingLocaleError": 258, @@ -63,6 +64,7 @@ export const ICU4XError_rust_to_js = { [0]: "UnknownError", [1]: "WriteableError", [2]: "OutOfBoundsError", + [3]: "Utf8Error", [256]: "DataMissingDataKeyError", [257]: "DataMissingVariantError", [258]: "DataMissingLocaleError", @@ -121,6 +123,7 @@ export const ICU4XError = { "UnknownError": "UnknownError", "WriteableError": "WriteableError", "OutOfBoundsError": "OutOfBoundsError", + "Utf8Error": "Utf8Error", "DataMissingDataKeyError": "DataMissingDataKeyError", "DataMissingVariantError": "DataMissingVariantError", "DataMissingLocaleError": "DataMissingLocaleError", diff --git a/ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.d.ts b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.d.ts new file mode 100644 index 00000000000..67f1faadf97 --- /dev/null +++ b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.d.ts @@ -0,0 +1,49 @@ +import { FFIError } from "./diplomat-runtime" +import { ICU4XDataProvider } from "./ICU4XDataProvider"; +import { ICU4XError } from "./ICU4XError"; + +/** + + * A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. + + * This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. It also supports normalizing and canonicalizing the IANA strings. + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapper.html Rust documentation for `TimeZoneIdMapper`} for more information. + */ +export class ICU4XTimeZoneIdMapper { + + /** + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapper.html#method.new Rust documentation for `new`} for more information. + * @throws {@link FFIError}<{@link ICU4XError}> + */ + static create(provider: ICU4XDataProvider): ICU4XTimeZoneIdMapper | never; + + /** + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.iana_to_bcp47 Rust documentation for `iana_to_bcp47`} for more information. + * @throws {@link FFIError}<{@link ICU4XError}> + */ + iana_to_bcp47(value: string): string | never; + + /** + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana Rust documentation for `normalize_iana`} for more information. + * @throws {@link FFIError}<{@link ICU4XError}> + */ + normalize_iana(value: string): string | never; + + /** + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana Rust documentation for `normalize_iana`} for more information. + * @throws {@link FFIError}<{@link ICU4XError}> + */ + canonicalize_iana(value: string): string | never; + + /** + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.find_canonical_iana_from_bcp47 Rust documentation for `find_canonical_iana_from_bcp47`} for more information. + * @throws {@link FFIError}<{@link ICU4XError}> + */ + find_canonical_iana_from_bcp47(value: string): string | never; +} diff --git a/ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.mjs b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.mjs new file mode 100644 index 00000000000..b15584169d8 --- /dev/null +++ b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.mjs @@ -0,0 +1,123 @@ +import wasm from "./diplomat-wasm.mjs" +import * as diplomatRuntime from "./diplomat-runtime.mjs" +import { ICU4XError_js_to_rust, ICU4XError_rust_to_js } from "./ICU4XError.mjs" + +const ICU4XTimeZoneIdMapper_box_destroy_registry = new FinalizationRegistry(underlying => { + wasm.ICU4XTimeZoneIdMapper_destroy(underlying); +}); + +export class ICU4XTimeZoneIdMapper { + #lifetimeEdges = []; + constructor(underlying, owned, edges) { + this.underlying = underlying; + this.#lifetimeEdges.push(...edges); + if (owned) { + ICU4XTimeZoneIdMapper_box_destroy_registry.register(this, underlying); + } + } + + static create(arg_provider) { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); + wasm.ICU4XTimeZoneIdMapper_create(diplomat_receive_buffer, arg_provider.underlying); + const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); + if (is_ok) { + const ok_value = new ICU4XTimeZoneIdMapper(diplomatRuntime.ptrRead(wasm, diplomat_receive_buffer), true, []); + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + return ok_value; + } else { + const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + throw new diplomatRuntime.FFIError(throw_value); + } + })(); + } + + iana_to_bcp47(arg_value) { + const buf_arg_value = diplomatRuntime.DiplomatBuf.str8(wasm, arg_value); + const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); + wasm.ICU4XTimeZoneIdMapper_iana_to_bcp47(diplomat_receive_buffer, this.underlying, buf_arg_value.ptr, buf_arg_value.size, writeable); + const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); + if (is_ok) { + const ok_value = {}; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + return ok_value; + } else { + const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + throw new diplomatRuntime.FFIError(throw_value); + } + })(); + }); + buf_arg_value.free(); + return diplomat_out; + } + + normalize_iana(arg_value) { + const buf_arg_value = diplomatRuntime.DiplomatBuf.str8(wasm, arg_value); + const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); + wasm.ICU4XTimeZoneIdMapper_normalize_iana(diplomat_receive_buffer, this.underlying, buf_arg_value.ptr, buf_arg_value.size, writeable); + const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); + if (is_ok) { + const ok_value = {}; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + return ok_value; + } else { + const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + throw new diplomatRuntime.FFIError(throw_value); + } + })(); + }); + buf_arg_value.free(); + return diplomat_out; + } + + canonicalize_iana(arg_value) { + const buf_arg_value = diplomatRuntime.DiplomatBuf.str8(wasm, arg_value); + const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); + wasm.ICU4XTimeZoneIdMapper_canonicalize_iana(diplomat_receive_buffer, this.underlying, buf_arg_value.ptr, buf_arg_value.size, writeable); + const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); + if (is_ok) { + const ok_value = {}; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + return ok_value; + } else { + const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + throw new diplomatRuntime.FFIError(throw_value); + } + })(); + }); + buf_arg_value.free(); + return diplomat_out; + } + + find_canonical_iana_from_bcp47(arg_value) { + const buf_arg_value = diplomatRuntime.DiplomatBuf.str8(wasm, arg_value); + const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); + wasm.ICU4XTimeZoneIdMapper_find_canonical_iana_from_bcp47(diplomat_receive_buffer, this.underlying, buf_arg_value.ptr, buf_arg_value.size, writeable); + const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); + if (is_ok) { + const ok_value = {}; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + return ok_value; + } else { + const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + throw new diplomatRuntime.FFIError(throw_value); + } + })(); + }); + buf_arg_value.free(); + return diplomat_out; + } +} diff --git a/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.d.ts b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.d.ts new file mode 100644 index 00000000000..763e87d0bbe --- /dev/null +++ b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.d.ts @@ -0,0 +1,43 @@ +import { FFIError } from "./diplomat-runtime" +import { ICU4XDataProvider } from "./ICU4XDataProvider"; +import { ICU4XError } from "./ICU4XError"; +import { ICU4XTimeZoneIdMapper } from "./ICU4XTimeZoneIdMapper"; + +/** + + * A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. + + * This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. It also supports normalizing and canonicalizing the IANA strings. + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalization.html Rust documentation for `TimeZoneIdMapperWithFastCanonicalization`} for more information. + */ +export class ICU4XTimeZoneIdMapperWithFastCanonicalization { + + /** + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalization.html#method.new Rust documentation for `new`} for more information. + * @throws {@link FFIError}<{@link ICU4XError}> + */ + static create(provider: ICU4XDataProvider): ICU4XTimeZoneIdMapper | never; + + /** + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.iana_to_bcp47 Rust documentation for `iana_to_bcp47`} for more information. + * @throws {@link FFIError}<{@link ICU4XError}> + */ + iana_to_bcp47(value: string): string | never; + + /** + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonicalize_iana Rust documentation for `canonicalize_iana`} for more information. + * @throws {@link FFIError}<{@link ICU4XError}> + */ + canonicalize_iana(value: string): string | never; + + /** + + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonical_iana_from_bcp47 Rust documentation for `canonical_iana_from_bcp47`} for more information. + * @throws {@link FFIError}<{@link ICU4XError}> + */ + find_canonical_iana_from_bcp47(value: string): string | never; +} diff --git a/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.mjs b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.mjs new file mode 100644 index 00000000000..e16f67af1b2 --- /dev/null +++ b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.mjs @@ -0,0 +1,102 @@ +import wasm from "./diplomat-wasm.mjs" +import * as diplomatRuntime from "./diplomat-runtime.mjs" +import { ICU4XError_js_to_rust, ICU4XError_rust_to_js } from "./ICU4XError.mjs" +import { ICU4XTimeZoneIdMapper } from "./ICU4XTimeZoneIdMapper.mjs" + +const ICU4XTimeZoneIdMapperWithFastCanonicalization_box_destroy_registry = new FinalizationRegistry(underlying => { + wasm.ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy(underlying); +}); + +export class ICU4XTimeZoneIdMapperWithFastCanonicalization { + #lifetimeEdges = []; + constructor(underlying, owned, edges) { + this.underlying = underlying; + this.#lifetimeEdges.push(...edges); + if (owned) { + ICU4XTimeZoneIdMapperWithFastCanonicalization_box_destroy_registry.register(this, underlying); + } + } + + static create(arg_provider) { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); + wasm.ICU4XTimeZoneIdMapperWithFastCanonicalization_create(diplomat_receive_buffer, arg_provider.underlying); + const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); + if (is_ok) { + const ok_value = new ICU4XTimeZoneIdMapper(diplomatRuntime.ptrRead(wasm, diplomat_receive_buffer), true, []); + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + return ok_value; + } else { + const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + throw new diplomatRuntime.FFIError(throw_value); + } + })(); + } + + iana_to_bcp47(arg_value) { + const buf_arg_value = diplomatRuntime.DiplomatBuf.str8(wasm, arg_value); + const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); + wasm.ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(diplomat_receive_buffer, this.underlying, buf_arg_value.ptr, buf_arg_value.size, writeable); + const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); + if (is_ok) { + const ok_value = {}; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + return ok_value; + } else { + const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + throw new diplomatRuntime.FFIError(throw_value); + } + })(); + }); + buf_arg_value.free(); + return diplomat_out; + } + + canonicalize_iana(arg_value) { + const buf_arg_value = diplomatRuntime.DiplomatBuf.str8(wasm, arg_value); + const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); + wasm.ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(diplomat_receive_buffer, this.underlying, buf_arg_value.ptr, buf_arg_value.size, writeable); + const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); + if (is_ok) { + const ok_value = {}; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + return ok_value; + } else { + const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + throw new diplomatRuntime.FFIError(throw_value); + } + })(); + }); + buf_arg_value.free(); + return diplomat_out; + } + + find_canonical_iana_from_bcp47(arg_value) { + const buf_arg_value = diplomatRuntime.DiplomatBuf.str8(wasm, arg_value); + const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); + wasm.ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(diplomat_receive_buffer, this.underlying, buf_arg_value.ptr, buf_arg_value.size, writeable); + const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); + if (is_ok) { + const ok_value = {}; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + return ok_value; + } else { + const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; + wasm.diplomat_free(diplomat_receive_buffer, 5, 4); + throw new diplomatRuntime.FFIError(throw_value); + } + })(); + }); + buf_arg_value.free(); + return diplomat_out; + } +} diff --git a/ffi/capi/bindings/js/index.d.ts b/ffi/capi/bindings/js/index.d.ts index 1219dac9121..55cdee8308e 100644 --- a/ffi/capi/bindings/js/index.d.ts +++ b/ffi/capi/bindings/js/index.d.ts @@ -110,6 +110,8 @@ export { ICU4XTime } from './ICU4XTime'; export { ICU4XTimeFormatter } from './ICU4XTimeFormatter'; export { ICU4XTimeLength } from './ICU4XTimeLength'; export { ICU4XTimeZoneFormatter } from './ICU4XTimeZoneFormatter'; +export { ICU4XTimeZoneIdMapper } from './ICU4XTimeZoneIdMapper'; +export { ICU4XTimeZoneIdMapperWithFastCanonicalization } from './ICU4XTimeZoneIdMapperWithFastCanonicalization'; export { ICU4XTitlecaseMapper } from './ICU4XTitlecaseMapper'; export { ICU4XTitlecaseOptionsV1 } from './ICU4XTitlecaseOptionsV1'; export { ICU4XTrailingCase } from './ICU4XTrailingCase'; diff --git a/ffi/capi/bindings/js/index.mjs b/ffi/capi/bindings/js/index.mjs index dff90a00ca9..69626c03815 100644 --- a/ffi/capi/bindings/js/index.mjs +++ b/ffi/capi/bindings/js/index.mjs @@ -110,6 +110,8 @@ export { ICU4XTime } from './ICU4XTime.mjs'; export { ICU4XTimeFormatter } from './ICU4XTimeFormatter.mjs'; export { ICU4XTimeLength } from './ICU4XTimeLength.mjs'; export { ICU4XTimeZoneFormatter } from './ICU4XTimeZoneFormatter.mjs'; +export { ICU4XTimeZoneIdMapper } from './ICU4XTimeZoneIdMapper.mjs'; +export { ICU4XTimeZoneIdMapperWithFastCanonicalization } from './ICU4XTimeZoneIdMapperWithFastCanonicalization.mjs'; export { ICU4XTitlecaseMapper } from './ICU4XTitlecaseMapper.mjs'; export { ICU4XTitlecaseOptionsV1 } from './ICU4XTitlecaseOptionsV1.mjs'; export { ICU4XTrailingCase } from './ICU4XTrailingCase.mjs'; diff --git a/ffi/capi/src/errors.rs b/ffi/capi/src/errors.rs index 617e2bcc9cf..4a4ae09858e 100644 --- a/ffi/capi/src/errors.rs +++ b/ffi/capi/src/errors.rs @@ -70,8 +70,10 @@ pub mod ffi { /// Typically found when not enough space is allocated /// Most APIs that return a string may return this error WriteableError = 0x01, - // Some input was out of bounds + /// Some input was out of bounds OutOfBoundsError = 0x02, + /// Input expected to be UTF-8 was ill-formed + Utf8Error = 0x03, // general data errors // See DataError @@ -208,6 +210,12 @@ impl From for ICU4XError { } } +impl From for ICU4XError { + fn from(_: core::str::Utf8Error) -> Self { + ICU4XError::Utf8Error + } +} + #[cfg(feature = "icu_collator")] impl From for ICU4XError { fn from(e: CollatorError) -> Self { diff --git a/ffi/capi/src/iana_bcp47_mapper.rs b/ffi/capi/src/iana_bcp47_mapper.rs index fd64e37c13e..8c78bcb6f68 100644 --- a/ffi/capi/src/iana_bcp47_mapper.rs +++ b/ffi/capi/src/iana_bcp47_mapper.rs @@ -2,6 +2,8 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). +#![allow(deprecated)] // these APIs are deprecated in Rust + #[diplomat::bridge] pub mod ffi { use crate::errors::ffi::ICU4XError; diff --git a/ffi/capi/src/lib.rs b/ffi/capi/src/lib.rs index 251b80fdd41..b400160e390 100644 --- a/ffi/capi/src/lib.rs +++ b/ffi/capi/src/lib.rs @@ -151,6 +151,8 @@ pub mod time; pub mod timezone; #[cfg(feature = "icu_datetime")] pub mod timezone_formatter; +#[cfg(feature = "icu_timezone")] +pub mod timezone_mapper; #[cfg(feature = "icu_calendar")] pub mod week; #[cfg(feature = "icu_datetime")] diff --git a/ffi/capi/src/timezone.rs b/ffi/capi/src/timezone.rs index 98e7e0b5d10..0bfe715e86b 100644 --- a/ffi/capi/src/timezone.rs +++ b/ffi/capi/src/timezone.rs @@ -158,6 +158,27 @@ pub mod ffi { Ok(()) } + // *** TODO: in 2.0 please replace try_set_iana_time_zone_id with try_set_iana_time_zone_id_2 *** + + /// Sets the `time_zone_id` field from an IANA string by looking up + /// the corresponding BCP-47 string. + /// + /// Errors if the string is not a valid BCP-47 time zone ID. + pub fn try_set_iana_time_zone_id_2( + &mut self, + mapper: &crate::timezone_mapper::ffi::ICU4XTimeZoneIdMapper, + id: &DiplomatStr, + ) -> Result<(), ICU4XError> { + self.0.time_zone_id = Some( + mapper + .0 + .as_borrowed() + .iana_bytes_to_bcp47(id) + .ok_or(ICU4XError::TimeZoneInvalidIdError)?, + ); + Ok(()) + } + /// Clears the `time_zone_id` field. #[diplomat::rust_link(icu::timezone::CustomTimeZone::time_zone_id, StructField)] #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id, Struct, compact)] diff --git a/ffi/capi/src/timezone_mapper.rs b/ffi/capi/src/timezone_mapper.rs new file mode 100644 index 00000000000..f0bcb0aea48 --- /dev/null +++ b/ffi/capi/src/timezone_mapper.rs @@ -0,0 +1,205 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +#[diplomat::bridge] +pub mod ffi { + use crate::errors::ffi::ICU4XError; + use crate::provider::ffi::ICU4XDataProvider; + use alloc::boxed::Box; + use icu_timezone::{TimeZoneBcp47Id, TimeZoneIdMapper}; + use tinystr::TinyAsciiStr; + + /// A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. + /// + /// This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. + /// It also supports normalizing and canonicalizing the IANA strings. + #[diplomat::opaque] + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapper, Struct)] + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapper::as_borrowed, FnInStruct, hidden)] + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperBorrowed, Struct, hidden)] + pub struct ICU4XTimeZoneIdMapper(pub TimeZoneIdMapper); + + impl ICU4XTimeZoneIdMapper { + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapper::new, FnInStruct)] + pub fn create( + provider: &ICU4XDataProvider, + ) -> Result, ICU4XError> { + Ok(Box::new(ICU4XTimeZoneIdMapper(call_constructor!( + TimeZoneIdMapper::new [r => Ok(r)], + TimeZoneIdMapper::try_new_with_any_provider, + TimeZoneIdMapper::try_new_with_buffer_provider, + provider, + )?))) + } + + #[diplomat::rust_link( + icu::timezone::TimeZoneIdMapperBorrowed::iana_to_bcp47, + FnInStruct + )] + #[diplomat::rust_link( + icu::timezone::TimeZoneIdMapperBorrowed::iana_bytes_to_bcp47, + FnInStruct, + hidden + )] + pub fn iana_to_bcp47( + &self, + value: &DiplomatStr, + write: &mut diplomat_runtime::DiplomatWriteable, + ) -> Result<(), ICU4XError> { + use writeable::Writeable; + let handle = self.0.as_borrowed(); + if let Some(s) = handle.iana_bytes_to_bcp47(value) { + Ok(s.0.write_to(write)?) + } else { + Err(ICU4XError::TimeZoneInvalidIdError) + } + } + + #[diplomat::rust_link( + icu::timezone::TimeZoneIdMapperBorrowed::normalize_iana, + FnInStruct + )] + pub fn normalize_iana( + &self, + value: &DiplomatStr, + write: &mut diplomat_runtime::DiplomatWriteable, + ) -> Result<(), ICU4XError> { + use writeable::Writeable; + let handle = self.0.as_borrowed(); + // Validate the UTF-8 here because it gets echoed back to the writeable + let value = core::str::from_utf8(value)?; + if let Some(s) = handle.normalize_iana(value) { + Ok(s.string.write_to(write)?) + } else { + Err(ICU4XError::TimeZoneInvalidIdError) + } + } + + #[diplomat::rust_link( + icu::timezone::TimeZoneIdMapperBorrowed::normalize_iana, + FnInStruct + )] + pub fn canonicalize_iana( + &self, + value: &DiplomatStr, + write: &mut diplomat_runtime::DiplomatWriteable, + ) -> Result<(), ICU4XError> { + use writeable::Writeable; + let handle = self.0.as_borrowed(); + // Validate the UTF-8 here because it gets echoed back to the writeable + let value = core::str::from_utf8(value)?; + if let Some(s) = handle.canonicalize_iana(value) { + Ok(s.string.write_to(write)?) + } else { + Err(ICU4XError::TimeZoneInvalidIdError) + } + } + + #[diplomat::rust_link( + icu::timezone::TimeZoneIdMapperBorrowed::find_canonical_iana_from_bcp47, + FnInStruct + )] + pub fn find_canonical_iana_from_bcp47( + &self, + value: &DiplomatStr, + write: &mut diplomat_runtime::DiplomatWriteable, + ) -> Result<(), ICU4XError> { + use writeable::Writeable; + let handle = self.0.as_borrowed(); + let bcp47_id = TimeZoneBcp47Id(TinyAsciiStr::from_bytes(value)?); + if let Some(s) = handle.find_canonical_iana_from_bcp47(bcp47_id) { + Ok(s.string.write_to(write)?) + } else { + Err(ICU4XError::TimeZoneInvalidIdError) + } + } + } + + + /// A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. + /// + /// This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. + /// It also supports normalizing and canonicalizing the IANA strings. + #[diplomat::opaque] + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization, Struct)] + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization::as_borrowed, FnInStruct, hidden)] + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalizationBorrowed, Struct, hidden)] + pub struct ICU4XTimeZoneIdMapperWithFastCanonicalization(pub TimeZoneIdMapper); + + impl ICU4XTimeZoneIdMapperWithFastCanonicalization { + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization::new, FnInStruct)] + pub fn create( + provider: &ICU4XDataProvider, + ) -> Result, ICU4XError> { + Ok(Box::new(ICU4XTimeZoneIdMapper(call_constructor!( + TimeZoneIdMapper::new [r => Ok(r)], + TimeZoneIdMapper::try_new_with_any_provider, + TimeZoneIdMapper::try_new_with_buffer_provider, + provider, + )?))) + } + + #[diplomat::rust_link( + icu::timezone::ICU4XTimeZoneIdMapperWithFastCanonicalization::iana_to_bcp47, + FnInStruct + )] + #[diplomat::rust_link( + icu::timezone::ICU4XTimeZoneIdMapperWithFastCanonicalization::iana_bytes_to_bcp47, + FnInStruct, + hidden + )] + pub fn iana_to_bcp47( + &self, + value: &DiplomatStr, + write: &mut diplomat_runtime::DiplomatWriteable, + ) -> Result<(), ICU4XError> { + use writeable::Writeable; + let handle = self.0.as_borrowed(); + if let Some(s) = handle.iana_bytes_to_bcp47(value) { + Ok(s.0.write_to(write)?) + } else { + Err(ICU4XError::TimeZoneInvalidIdError) + } + } + + #[diplomat::rust_link( + icu::timezone::ICU4XTimeZoneIdMapperWithFastCanonicalization::canonicalize_iana, + FnInStruct + )] + pub fn canonicalize_iana( + &self, + value: &DiplomatStr, + write: &mut diplomat_runtime::DiplomatWriteable, + ) -> Result<(), ICU4XError> { + use writeable::Writeable; + let handle = self.0.as_borrowed(); + // Validate the UTF-8 here because it gets echoed back to the writeable + let value = core::str::from_utf8(value)?; + if let Some(s) = handle.canonicalize_iana(value) { + Ok(s.string.write_to(write)?) + } else { + Err(ICU4XError::TimeZoneInvalidIdError) + } + } + + #[diplomat::rust_link( + icu::timezone::ICU4XTimeZoneIdMapperWithFastCanonicalization::canonical_iana_from_bcp47, + FnInStruct + )] + pub fn find_canonical_iana_from_bcp47( + &self, + value: &DiplomatStr, + write: &mut diplomat_runtime::DiplomatWriteable, + ) -> Result<(), ICU4XError> { + use writeable::Writeable; + let handle = self.0.as_borrowed(); + let bcp47_id = TimeZoneBcp47Id(TinyAsciiStr::from_bytes(value)?); + if let Some(s) = handle.find_canonical_iana_from_bcp47(bcp47_id) { + Ok(s.string.write_to(write)?) + } else { + Err(ICU4XError::TimeZoneInvalidIdError) + } + } + } +} From 8629048ad5586445b66f3417553cbb4f1b53b005 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 17:00:48 -0700 Subject: [PATCH 18/31] FFI fixes --- ...TimeZoneIdMapperWithFastCanonicalization.h | 8 +-- ...apperWithFastCanonicalization_ICU4XError.h | 26 ++++++++ .../bindings/cpp/ICU4XTimeZoneIdMapper.hpp | 4 +- ...TimeZoneIdMapperWithFastCanonicalization.h | 8 +-- ...meZoneIdMapperWithFastCanonicalization.hpp | 64 +++++-------------- ...apperWithFastCanonicalization_ICU4XError.h | 26 ++++++++ .../bindings/dart/TimeZoneIdMapper.g.dart | 2 +- ...oneIdMapperWithFastCanonicalization.g.dart | 38 +++-------- .../bindings/js/ICU4XTimeZoneIdMapper.d.ts | 2 +- ...eZoneIdMapperWithFastCanonicalization.d.ts | 16 ++--- ...meZoneIdMapperWithFastCanonicalization.mjs | 29 +-------- ffi/capi/src/timezone.rs | 2 + ffi/capi/src/timezone_mapper.rs | 50 +++++---------- tools/ffi_coverage/src/allowlist.rs | 3 + tutorials/cpp/Cargo.lock | 4 ++ tutorials/cpp/datetime.cpp | 30 +++++++-- 16 files changed, 140 insertions(+), 172 deletions(-) create mode 100644 ffi/capi/bindings/c/diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError.h create mode 100644 ffi/capi/bindings/cpp/diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError.h diff --git a/ffi/capi/bindings/c/ICU4XTimeZoneIdMapperWithFastCanonicalization.h b/ffi/capi/bindings/c/ICU4XTimeZoneIdMapperWithFastCanonicalization.h index 8d5ec644667..c8091d4b645 100644 --- a/ffi/capi/bindings/c/ICU4XTimeZoneIdMapperWithFastCanonicalization.h +++ b/ffi/capi/bindings/c/ICU4XTimeZoneIdMapperWithFastCanonicalization.h @@ -15,20 +15,18 @@ typedef struct ICU4XTimeZoneIdMapperWithFastCanonicalization ICU4XTimeZoneIdMapp } // namespace capi #endif #include "ICU4XDataProvider.h" -#include "diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h" +#include "diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError.h" #include "diplomat_result_void_ICU4XError.h" #ifdef __cplusplus namespace capi { extern "C" { #endif -diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_create(const ICU4XDataProvider* provider); - -diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); +diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_create(const ICU4XDataProvider* provider); diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); -diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_canonical_iana_from_bcp47(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); void ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy(ICU4XTimeZoneIdMapperWithFastCanonicalization* self); #ifdef __cplusplus diff --git a/ffi/capi/bindings/c/diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError.h b/ffi/capi/bindings/c/diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError.h new file mode 100644 index 00000000000..181c9cceac1 --- /dev/null +++ b/ffi/capi/bindings/c/diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError.h @@ -0,0 +1,26 @@ +#ifndef diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError_H +#define diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#include "ICU4XTimeZoneIdMapperWithFastCanonicalization.h" +#include "ICU4XError.h" +#ifdef __cplusplus +namespace capi { +extern "C" { +#endif +typedef struct diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError { + union { + ICU4XTimeZoneIdMapperWithFastCanonicalization* ok; + ICU4XError err; + }; + bool is_ok; +} diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError; +#ifdef __cplusplus +} // extern "C" +} // namespace capi +#endif +#endif diff --git a/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.hpp b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.hpp index b4b7bce0b28..7a43b61340f 100644 --- a/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.hpp +++ b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapper.hpp @@ -61,12 +61,12 @@ class ICU4XTimeZoneIdMapper { diplomat::result normalize_iana(const std::string_view value) const; /** - * See the [Rust documentation for `normalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana) for more information. + * See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.canonicalize_iana) for more information. */ template diplomat::result canonicalize_iana_to_writeable(const std::string_view value, W& write) const; /** - * See the [Rust documentation for `normalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana) for more information. + * See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.canonicalize_iana) for more information. */ diplomat::result canonicalize_iana(const std::string_view value) const; diff --git a/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.h b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.h index 8d5ec644667..c8091d4b645 100644 --- a/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.h +++ b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.h @@ -15,20 +15,18 @@ typedef struct ICU4XTimeZoneIdMapperWithFastCanonicalization ICU4XTimeZoneIdMapp } // namespace capi #endif #include "ICU4XDataProvider.h" -#include "diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError.h" +#include "diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError.h" #include "diplomat_result_void_ICU4XError.h" #ifdef __cplusplus namespace capi { extern "C" { #endif -diplomat_result_box_ICU4XTimeZoneIdMapper_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_create(const ICU4XDataProvider* provider); - -diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); +diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_create(const ICU4XDataProvider* provider); diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); -diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); +diplomat_result_void_ICU4XError ICU4XTimeZoneIdMapperWithFastCanonicalization_canonical_iana_from_bcp47(const ICU4XTimeZoneIdMapperWithFastCanonicalization* self, const char* value_data, size_t value_len, DiplomatWriteable* write); void ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy(ICU4XTimeZoneIdMapperWithFastCanonicalization* self); #ifdef __cplusplus diff --git a/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.hpp b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.hpp index 1e58e187e5e..9c39e2bd782 100644 --- a/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.hpp +++ b/ffi/capi/bindings/cpp/ICU4XTimeZoneIdMapperWithFastCanonicalization.hpp @@ -12,7 +12,7 @@ #include "ICU4XTimeZoneIdMapperWithFastCanonicalization.h" class ICU4XDataProvider; -class ICU4XTimeZoneIdMapper; +class ICU4XTimeZoneIdMapperWithFastCanonicalization; #include "ICU4XError.hpp" /** @@ -38,37 +38,27 @@ class ICU4XTimeZoneIdMapperWithFastCanonicalization { /** * See the [Rust documentation for `new`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalization.html#method.new) for more information. */ - static diplomat::result create(const ICU4XDataProvider& provider); + static diplomat::result create(const ICU4XDataProvider& provider); /** - * See the [Rust documentation for `iana_to_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.iana_to_bcp47) for more information. - */ - template diplomat::result iana_to_bcp47_to_writeable(const std::string_view value, W& write) const; - - /** - * See the [Rust documentation for `iana_to_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.iana_to_bcp47) for more information. - */ - diplomat::result iana_to_bcp47(const std::string_view value) const; - - /** - * See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonicalize_iana) for more information. + * See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalizationBorrowed.html#method.canonicalize_iana) for more information. */ template diplomat::result canonicalize_iana_to_writeable(const std::string_view value, W& write) const; /** - * See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonicalize_iana) for more information. + * See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalizationBorrowed.html#method.canonicalize_iana) for more information. */ diplomat::result canonicalize_iana(const std::string_view value) const; /** - * See the [Rust documentation for `canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonical_iana_from_bcp47) for more information. + * See the [Rust documentation for `canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalizationBorrowed.html#method.canonical_iana_from_bcp47) for more information. */ - template diplomat::result find_canonical_iana_from_bcp47_to_writeable(const std::string_view value, W& write) const; + template diplomat::result canonical_iana_from_bcp47_to_writeable(const std::string_view value, W& write) const; /** - * See the [Rust documentation for `canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonical_iana_from_bcp47) for more information. + * See the [Rust documentation for `canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalizationBorrowed.html#method.canonical_iana_from_bcp47) for more information. */ - diplomat::result find_canonical_iana_from_bcp47(const std::string_view value) const; + diplomat::result canonical_iana_from_bcp47(const std::string_view value) const; inline const capi::ICU4XTimeZoneIdMapperWithFastCanonicalization* AsFFI() const { return this->inner.get(); } inline capi::ICU4XTimeZoneIdMapperWithFastCanonicalization* AsFFIMut() { return this->inner.get(); } inline explicit ICU4XTimeZoneIdMapperWithFastCanonicalization(capi::ICU4XTimeZoneIdMapperWithFastCanonicalization* i) : inner(i) {} @@ -80,41 +70,17 @@ class ICU4XTimeZoneIdMapperWithFastCanonicalization { }; #include "ICU4XDataProvider.hpp" -#include "ICU4XTimeZoneIdMapper.hpp" -inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::create(const ICU4XDataProvider& provider) { +inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::create(const ICU4XDataProvider& provider) { auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_create(provider.AsFFI()); - diplomat::result diplomat_result_out_value; + diplomat::result diplomat_result_out_value; if (diplomat_result_raw_out_value.is_ok) { - diplomat_result_out_value = diplomat::Ok(ICU4XTimeZoneIdMapper(diplomat_result_raw_out_value.ok)); + diplomat_result_out_value = diplomat::Ok(ICU4XTimeZoneIdMapperWithFastCanonicalization(diplomat_result_raw_out_value.ok)); } else { diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); } return diplomat_result_out_value; } -template inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::iana_to_bcp47_to_writeable(const std::string_view value, W& write) const { - capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); - auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(this->inner.get(), value.data(), value.size(), &write_writer); - diplomat::result diplomat_result_out_value; - if (diplomat_result_raw_out_value.is_ok) { - diplomat_result_out_value = diplomat::Ok(std::monostate()); - } else { - diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); - } - return diplomat_result_out_value; -} -inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::iana_to_bcp47(const std::string_view value) const { - std::string diplomat_writeable_string; - capi::DiplomatWriteable diplomat_writeable_out = diplomat::WriteableFromString(diplomat_writeable_string); - auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(this->inner.get(), value.data(), value.size(), &diplomat_writeable_out); - diplomat::result diplomat_result_out_value; - if (diplomat_result_raw_out_value.is_ok) { - diplomat_result_out_value = diplomat::Ok(std::monostate()); - } else { - diplomat_result_out_value = diplomat::Err(static_cast(diplomat_result_raw_out_value.err)); - } - return diplomat_result_out_value.replace_ok(std::move(diplomat_writeable_string)); -} template inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::canonicalize_iana_to_writeable(const std::string_view value, W& write) const { capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(this->inner.get(), value.data(), value.size(), &write_writer); @@ -138,9 +104,9 @@ inline diplomat::result ICU4XTimeZoneIdMapperWithFastCa } return diplomat_result_out_value.replace_ok(std::move(diplomat_writeable_string)); } -template inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::find_canonical_iana_from_bcp47_to_writeable(const std::string_view value, W& write) const { +template inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::canonical_iana_from_bcp47_to_writeable(const std::string_view value, W& write) const { capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); - auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(this->inner.get(), value.data(), value.size(), &write_writer); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_canonical_iana_from_bcp47(this->inner.get(), value.data(), value.size(), &write_writer); diplomat::result diplomat_result_out_value; if (diplomat_result_raw_out_value.is_ok) { diplomat_result_out_value = diplomat::Ok(std::monostate()); @@ -149,10 +115,10 @@ template inline diplomat::result ICU4XTi } return diplomat_result_out_value; } -inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::find_canonical_iana_from_bcp47(const std::string_view value) const { +inline diplomat::result ICU4XTimeZoneIdMapperWithFastCanonicalization::canonical_iana_from_bcp47(const std::string_view value) const { std::string diplomat_writeable_string; capi::DiplomatWriteable diplomat_writeable_out = diplomat::WriteableFromString(diplomat_writeable_string); - auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(this->inner.get(), value.data(), value.size(), &diplomat_writeable_out); + auto diplomat_result_raw_out_value = capi::ICU4XTimeZoneIdMapperWithFastCanonicalization_canonical_iana_from_bcp47(this->inner.get(), value.data(), value.size(), &diplomat_writeable_out); diplomat::result diplomat_result_out_value; if (diplomat_result_raw_out_value.is_ok) { diplomat_result_out_value = diplomat::Ok(std::monostate()); diff --git a/ffi/capi/bindings/cpp/diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError.h b/ffi/capi/bindings/cpp/diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError.h new file mode 100644 index 00000000000..181c9cceac1 --- /dev/null +++ b/ffi/capi/bindings/cpp/diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError.h @@ -0,0 +1,26 @@ +#ifndef diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError_H +#define diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#include "ICU4XTimeZoneIdMapperWithFastCanonicalization.h" +#include "ICU4XError.h" +#ifdef __cplusplus +namespace capi { +extern "C" { +#endif +typedef struct diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError { + union { + ICU4XTimeZoneIdMapperWithFastCanonicalization* ok; + ICU4XError err; + }; + bool is_ok; +} diplomat_result_box_ICU4XTimeZoneIdMapperWithFastCanonicalization_ICU4XError; +#ifdef __cplusplus +} // extern "C" +} // namespace capi +#endif +#endif diff --git a/ffi/capi/bindings/dart/TimeZoneIdMapper.g.dart b/ffi/capi/bindings/dart/TimeZoneIdMapper.g.dart index 0d7b5e8c14c..057d2ca0a56 100644 --- a/ffi/capi/bindings/dart/TimeZoneIdMapper.g.dart +++ b/ffi/capi/bindings/dart/TimeZoneIdMapper.g.dart @@ -68,7 +68,7 @@ final class TimeZoneIdMapper implements ffi.Finalizable { return writeable.finalize(); } - /// See the [Rust documentation for `normalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana) for more information. + /// See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.canonicalize_iana) for more information. /// /// Throws [Error] on failure. String canonicalizeIana(String value) { diff --git a/ffi/capi/bindings/dart/TimeZoneIdMapperWithFastCanonicalization.g.dart b/ffi/capi/bindings/dart/TimeZoneIdMapperWithFastCanonicalization.g.dart index cc08a4c2c88..88a01eeffe8 100644 --- a/ffi/capi/bindings/dart/TimeZoneIdMapperWithFastCanonicalization.g.dart +++ b/ffi/capi/bindings/dart/TimeZoneIdMapperWithFastCanonicalization.g.dart @@ -30,30 +30,15 @@ final class TimeZoneIdMapperWithFastCanonicalization implements ffi.Finalizable /// See the [Rust documentation for `new`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalization.html#method.new) for more information. /// /// Throws [Error] on failure. - static TimeZoneIdMapper create(DataProvider provider) { + factory TimeZoneIdMapperWithFastCanonicalization(DataProvider provider) { final result = _ICU4XTimeZoneIdMapperWithFastCanonicalization_create(provider._ffi); if (!result.isOk) { throw Error.values.firstWhere((v) => v._ffi == result.union.err); } - return TimeZoneIdMapper._fromFfi(result.union.ok, []); + return TimeZoneIdMapperWithFastCanonicalization._fromFfi(result.union.ok, []); } - /// See the [Rust documentation for `iana_to_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.iana_to_bcp47) for more information. - /// - /// Throws [Error] on failure. - String ianaToBcp47(String value) { - final temp = ffi2.Arena(); - final valueView = value.utf8View; - final writeable = _Writeable(); - final result = _ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(_ffi, valueView.allocIn(temp), valueView.length, writeable._ffi); - temp.releaseAll(); - if (!result.isOk) { - throw Error.values.firstWhere((v) => v._ffi == result.union.err); - } - return writeable.finalize(); - } - - /// See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonicalize_iana) for more information. + /// See the [Rust documentation for `canonicalize_iana`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalizationBorrowed.html#method.canonicalize_iana) for more information. /// /// Throws [Error] on failure. String canonicalizeIana(String value) { @@ -68,14 +53,14 @@ final class TimeZoneIdMapperWithFastCanonicalization implements ffi.Finalizable return writeable.finalize(); } - /// See the [Rust documentation for `canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonical_iana_from_bcp47) for more information. + /// See the [Rust documentation for `canonical_iana_from_bcp47`](https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalizationBorrowed.html#method.canonical_iana_from_bcp47) for more information. /// /// Throws [Error] on failure. - String findCanonicalIanaFromBcp47(String value) { + String canonicalIanaFromBcp47(String value) { final temp = ffi2.Arena(); final valueView = value.utf8View; final writeable = _Writeable(); - final result = _ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(_ffi, valueView.allocIn(temp), valueView.length, writeable._ffi); + final result = _ICU4XTimeZoneIdMapperWithFastCanonicalization_canonical_iana_from_bcp47(_ffi, valueView.allocIn(temp), valueView.length, writeable._ffi); temp.releaseAll(); if (!result.isOk) { throw Error.values.firstWhere((v) => v._ffi == result.union.err); @@ -94,17 +79,12 @@ external void _ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy(ffi.Pointer // ignore: non_constant_identifier_names external _ResultOpaqueInt32 _ICU4XTimeZoneIdMapperWithFastCanonicalization_create(ffi.Pointer provider); -@meta.ResourceIdentifier('ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47') -@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47') -// ignore: non_constant_identifier_names -external _ResultVoidInt32 _ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); - @meta.ResourceIdentifier('ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana') @ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana') // ignore: non_constant_identifier_names external _ResultVoidInt32 _ICU4XTimeZoneIdMapperWithFastCanonicalization_canonicalize_iana(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); -@meta.ResourceIdentifier('ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47') -@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47') +@meta.ResourceIdentifier('ICU4XTimeZoneIdMapperWithFastCanonicalization_canonical_iana_from_bcp47') +@ffi.Native<_ResultVoidInt32 Function(ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Pointer)>(isLeaf: true, symbol: 'ICU4XTimeZoneIdMapperWithFastCanonicalization_canonical_iana_from_bcp47') // ignore: non_constant_identifier_names -external _ResultVoidInt32 _ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); +external _ResultVoidInt32 _ICU4XTimeZoneIdMapperWithFastCanonicalization_canonical_iana_from_bcp47(ffi.Pointer self, ffi.Pointer valueData, int valueLength, ffi.Pointer writeable); diff --git a/ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.d.ts b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.d.ts index 67f1faadf97..3424fcd61cc 100644 --- a/ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.d.ts +++ b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapper.d.ts @@ -35,7 +35,7 @@ export class ICU4XTimeZoneIdMapper { /** - * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.normalize_iana Rust documentation for `normalize_iana`} for more information. + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperBorrowed.html#method.canonicalize_iana Rust documentation for `canonicalize_iana`} for more information. * @throws {@link FFIError}<{@link ICU4XError}> */ canonicalize_iana(value: string): string | never; diff --git a/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.d.ts b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.d.ts index 763e87d0bbe..b6cadd9a0f1 100644 --- a/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.d.ts +++ b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.d.ts @@ -1,7 +1,6 @@ import { FFIError } from "./diplomat-runtime" import { ICU4XDataProvider } from "./ICU4XDataProvider"; import { ICU4XError } from "./ICU4XError"; -import { ICU4XTimeZoneIdMapper } from "./ICU4XTimeZoneIdMapper"; /** @@ -18,26 +17,19 @@ export class ICU4XTimeZoneIdMapperWithFastCanonicalization { * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalization.html#method.new Rust documentation for `new`} for more information. * @throws {@link FFIError}<{@link ICU4XError}> */ - static create(provider: ICU4XDataProvider): ICU4XTimeZoneIdMapper | never; + static create(provider: ICU4XDataProvider): ICU4XTimeZoneIdMapperWithFastCanonicalization | never; /** - * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.iana_to_bcp47 Rust documentation for `iana_to_bcp47`} for more information. - * @throws {@link FFIError}<{@link ICU4XError}> - */ - iana_to_bcp47(value: string): string | never; - - /** - - * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonicalize_iana Rust documentation for `canonicalize_iana`} for more information. + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalizationBorrowed.html#method.canonicalize_iana Rust documentation for `canonicalize_iana`} for more information. * @throws {@link FFIError}<{@link ICU4XError}> */ canonicalize_iana(value: string): string | never; /** - * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.ICU4XTimeZoneIdMapperWithFastCanonicalization.html#method.canonical_iana_from_bcp47 Rust documentation for `canonical_iana_from_bcp47`} for more information. + * See the {@link https://docs.rs/icu/latest/icu/timezone/struct.TimeZoneIdMapperWithFastCanonicalizationBorrowed.html#method.canonical_iana_from_bcp47 Rust documentation for `canonical_iana_from_bcp47`} for more information. * @throws {@link FFIError}<{@link ICU4XError}> */ - find_canonical_iana_from_bcp47(value: string): string | never; + canonical_iana_from_bcp47(value: string): string | never; } diff --git a/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.mjs b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.mjs index e16f67af1b2..14ceb79bf88 100644 --- a/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.mjs +++ b/ffi/capi/bindings/js/ICU4XTimeZoneIdMapperWithFastCanonicalization.mjs @@ -1,7 +1,6 @@ import wasm from "./diplomat-wasm.mjs" import * as diplomatRuntime from "./diplomat-runtime.mjs" import { ICU4XError_js_to_rust, ICU4XError_rust_to_js } from "./ICU4XError.mjs" -import { ICU4XTimeZoneIdMapper } from "./ICU4XTimeZoneIdMapper.mjs" const ICU4XTimeZoneIdMapperWithFastCanonicalization_box_destroy_registry = new FinalizationRegistry(underlying => { wasm.ICU4XTimeZoneIdMapperWithFastCanonicalization_destroy(underlying); @@ -23,7 +22,7 @@ export class ICU4XTimeZoneIdMapperWithFastCanonicalization { wasm.ICU4XTimeZoneIdMapperWithFastCanonicalization_create(diplomat_receive_buffer, arg_provider.underlying); const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); if (is_ok) { - const ok_value = new ICU4XTimeZoneIdMapper(diplomatRuntime.ptrRead(wasm, diplomat_receive_buffer), true, []); + const ok_value = new ICU4XTimeZoneIdMapperWithFastCanonicalization(diplomatRuntime.ptrRead(wasm, diplomat_receive_buffer), true, []); wasm.diplomat_free(diplomat_receive_buffer, 5, 4); return ok_value; } else { @@ -34,28 +33,6 @@ export class ICU4XTimeZoneIdMapperWithFastCanonicalization { })(); } - iana_to_bcp47(arg_value) { - const buf_arg_value = diplomatRuntime.DiplomatBuf.str8(wasm, arg_value); - const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { - return (() => { - const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); - wasm.ICU4XTimeZoneIdMapperWithFastCanonicalization_iana_to_bcp47(diplomat_receive_buffer, this.underlying, buf_arg_value.ptr, buf_arg_value.size, writeable); - const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); - if (is_ok) { - const ok_value = {}; - wasm.diplomat_free(diplomat_receive_buffer, 5, 4); - return ok_value; - } else { - const throw_value = ICU4XError_rust_to_js[diplomatRuntime.enumDiscriminant(wasm, diplomat_receive_buffer)]; - wasm.diplomat_free(diplomat_receive_buffer, 5, 4); - throw new diplomatRuntime.FFIError(throw_value); - } - })(); - }); - buf_arg_value.free(); - return diplomat_out; - } - canonicalize_iana(arg_value) { const buf_arg_value = diplomatRuntime.DiplomatBuf.str8(wasm, arg_value); const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { @@ -78,12 +55,12 @@ export class ICU4XTimeZoneIdMapperWithFastCanonicalization { return diplomat_out; } - find_canonical_iana_from_bcp47(arg_value) { + canonical_iana_from_bcp47(arg_value) { const buf_arg_value = diplomatRuntime.DiplomatBuf.str8(wasm, arg_value); const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { return (() => { const diplomat_receive_buffer = wasm.diplomat_alloc(5, 4); - wasm.ICU4XTimeZoneIdMapperWithFastCanonicalization_find_canonical_iana_from_bcp47(diplomat_receive_buffer, this.underlying, buf_arg_value.ptr, buf_arg_value.size, writeable); + wasm.ICU4XTimeZoneIdMapperWithFastCanonicalization_canonical_iana_from_bcp47(diplomat_receive_buffer, this.underlying, buf_arg_value.ptr, buf_arg_value.size, writeable); const is_ok = diplomatRuntime.resultFlag(wasm, diplomat_receive_buffer, 4); if (is_ok) { const ok_value = {}; diff --git a/ffi/capi/src/timezone.rs b/ffi/capi/src/timezone.rs index 0bfe715e86b..cc7b847118f 100644 --- a/ffi/capi/src/timezone.rs +++ b/ffi/capi/src/timezone.rs @@ -130,6 +130,8 @@ pub mod ffi { #[diplomat::rust_link(icu::timezone::CustomTimeZone::time_zone_id, StructField)] #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id, Struct, compact)] #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id::from_str, FnInStruct, hidden)] + #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id::deref, FnInStruct, hidden)] + #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id::Target, AssociatedTypeInStruct, hidden)] pub fn try_set_time_zone_id(&mut self, id: &DiplomatStr) -> Result<(), ICU4XError> { self.0.time_zone_id = Some(icu_timezone::TimeZoneBcp47Id( tinystr::TinyAsciiStr::from_bytes(id)?, diff --git a/ffi/capi/src/timezone_mapper.rs b/ffi/capi/src/timezone_mapper.rs index f0bcb0aea48..3fb0068bd2b 100644 --- a/ffi/capi/src/timezone_mapper.rs +++ b/ffi/capi/src/timezone_mapper.rs @@ -7,7 +7,7 @@ pub mod ffi { use crate::errors::ffi::ICU4XError; use crate::provider::ffi::ICU4XDataProvider; use alloc::boxed::Box; - use icu_timezone::{TimeZoneBcp47Id, TimeZoneIdMapper}; + use icu_timezone::{TimeZoneBcp47Id, TimeZoneIdMapper, TimeZoneIdMapperWithFastCanonicalization}; use tinystr::TinyAsciiStr; /// A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. @@ -18,6 +18,7 @@ pub mod ffi { #[diplomat::rust_link(icu::timezone::TimeZoneIdMapper, Struct)] #[diplomat::rust_link(icu::timezone::TimeZoneIdMapper::as_borrowed, FnInStruct, hidden)] #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperBorrowed, Struct, hidden)] + #[diplomat::rust_link(icu::timezone::NormalizedIana, Struct, hidden)] pub struct ICU4XTimeZoneIdMapper(pub TimeZoneIdMapper); impl ICU4XTimeZoneIdMapper { @@ -77,7 +78,7 @@ pub mod ffi { } #[diplomat::rust_link( - icu::timezone::TimeZoneIdMapperBorrowed::normalize_iana, + icu::timezone::TimeZoneIdMapperBorrowed::canonicalize_iana, FnInStruct )] pub fn canonicalize_iana( @@ -124,47 +125,26 @@ pub mod ffi { #[diplomat::opaque] #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization, Struct)] #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization::as_borrowed, FnInStruct, hidden)] + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization::inner, FnInStruct, hidden)] #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalizationBorrowed, Struct, hidden)] - pub struct ICU4XTimeZoneIdMapperWithFastCanonicalization(pub TimeZoneIdMapper); + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalizationBorrowed::inner, FnInStruct, hidden)] + pub struct ICU4XTimeZoneIdMapperWithFastCanonicalization(pub TimeZoneIdMapperWithFastCanonicalization); impl ICU4XTimeZoneIdMapperWithFastCanonicalization { #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization::new, FnInStruct)] pub fn create( provider: &ICU4XDataProvider, - ) -> Result, ICU4XError> { - Ok(Box::new(ICU4XTimeZoneIdMapper(call_constructor!( - TimeZoneIdMapper::new [r => Ok(r)], - TimeZoneIdMapper::try_new_with_any_provider, - TimeZoneIdMapper::try_new_with_buffer_provider, + ) -> Result, ICU4XError> { + Ok(Box::new(ICU4XTimeZoneIdMapperWithFastCanonicalization(call_constructor!( + TimeZoneIdMapperWithFastCanonicalization::new [r => Ok(r)], + TimeZoneIdMapperWithFastCanonicalization::try_new_with_any_provider, + TimeZoneIdMapperWithFastCanonicalization::try_new_with_buffer_provider, provider, )?))) } #[diplomat::rust_link( - icu::timezone::ICU4XTimeZoneIdMapperWithFastCanonicalization::iana_to_bcp47, - FnInStruct - )] - #[diplomat::rust_link( - icu::timezone::ICU4XTimeZoneIdMapperWithFastCanonicalization::iana_bytes_to_bcp47, - FnInStruct, - hidden - )] - pub fn iana_to_bcp47( - &self, - value: &DiplomatStr, - write: &mut diplomat_runtime::DiplomatWriteable, - ) -> Result<(), ICU4XError> { - use writeable::Writeable; - let handle = self.0.as_borrowed(); - if let Some(s) = handle.iana_bytes_to_bcp47(value) { - Ok(s.0.write_to(write)?) - } else { - Err(ICU4XError::TimeZoneInvalidIdError) - } - } - - #[diplomat::rust_link( - icu::timezone::ICU4XTimeZoneIdMapperWithFastCanonicalization::canonicalize_iana, + icu::timezone::TimeZoneIdMapperWithFastCanonicalizationBorrowed::canonicalize_iana, FnInStruct )] pub fn canonicalize_iana( @@ -184,10 +164,10 @@ pub mod ffi { } #[diplomat::rust_link( - icu::timezone::ICU4XTimeZoneIdMapperWithFastCanonicalization::canonical_iana_from_bcp47, + icu::timezone::TimeZoneIdMapperWithFastCanonicalizationBorrowed::canonical_iana_from_bcp47, FnInStruct )] - pub fn find_canonical_iana_from_bcp47( + pub fn canonical_iana_from_bcp47( &self, value: &DiplomatStr, write: &mut diplomat_runtime::DiplomatWriteable, @@ -195,7 +175,7 @@ pub mod ffi { use writeable::Writeable; let handle = self.0.as_borrowed(); let bcp47_id = TimeZoneBcp47Id(TinyAsciiStr::from_bytes(value)?); - if let Some(s) = handle.find_canonical_iana_from_bcp47(bcp47_id) { + if let Some(s) = handle.canonical_iana_from_bcp47(bcp47_id) { Ok(s.string.write_to(write)?) } else { Err(ICU4XError::TimeZoneInvalidIdError) diff --git a/tools/ffi_coverage/src/allowlist.rs b/tools/ffi_coverage/src/allowlist.rs index 137f98604f2..41b28b29fa5 100644 --- a/tools/ffi_coverage/src/allowlist.rs +++ b/tools/ffi_coverage/src/allowlist.rs @@ -429,6 +429,9 @@ lazy_static::lazy_static! { "icu_provider_adapters::fork::MultiForkByErrorProvider", "icu_provider_adapters::fork::MultiForkByKeyProvider", + // Specialized constructor for separately constructed instances + "icu::timezone::TimeZoneIdMapperWithFastCanonicalization::try_new_with_mapper", + // macros "icu::locid::langid", "icu::locid::locale", diff --git a/tutorials/cpp/Cargo.lock b/tutorials/cpp/Cargo.lock index e3639e95341..16f335f5339 100644 --- a/tutorials/cpp/Cargo.lock +++ b/tutorials/cpp/Cargo.lock @@ -403,6 +403,7 @@ name = "icu_pattern" version = "0.1.5" dependencies = [ "displaydoc", + "either", "serde", "writeable", "yoke", @@ -1044,6 +1045,9 @@ checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" version = "0.5.4" +dependencies = [ + "either", +] [[package]] name = "yoke" diff --git a/tutorials/cpp/datetime.cpp b/tutorials/cpp/datetime.cpp index 62b815ca8a5..9c756e66bc3 100644 --- a/tutorials/cpp/datetime.cpp +++ b/tutorials/cpp/datetime.cpp @@ -9,7 +9,8 @@ #include "ICU4XDataStruct.hpp" #include "ICU4XLogger.hpp" #include "ICU4XCustomTimeZone.hpp" -#include "ICU4XIanaToBcp47Mapper.hpp" +#include "ICU4XTimeZoneIdMapper.hpp" +#include "ICU4XTimeZoneIdMapperWithFastCanonicalization.hpp" #include "ICU4XBcp47ToIanaMapper.hpp" #include "ICU4XGregorianZonedDateTimeFormatter.hpp" #include "ICU4XZonedDateTimeFormatter.hpp" @@ -68,17 +69,32 @@ int main() { return 1; } ICU4XMetazoneCalculator mzcalc = ICU4XMetazoneCalculator::create(dp).ok().value(); - ICU4XIanaToBcp47Mapper mapper = ICU4XIanaToBcp47Mapper::create(dp).ok().value(); - time_zone.try_set_iana_time_zone_id(mapper, "america/chicago").ok().value(); + ICU4XTimeZoneIdMapper mapper = ICU4XTimeZoneIdMapper::create(dp).ok().value(); + time_zone.try_set_iana_time_zone_id_2(mapper, "america/chicago").ok().value(); std::string time_zone_id_return = time_zone.time_zone_id().ok().value(); if (time_zone_id_return != "uschi") { std::cout << "Time zone ID does not roundtrip: " << time_zone_id_return << std::endl; return 1; } - ICU4XBcp47ToIanaMapper reverse_mapper = ICU4XBcp47ToIanaMapper::create(dp).ok().value(); - std::string recovered_iana_id = reverse_mapper.get("uschi").ok().value(); - if (recovered_iana_id != "America/Chicago") { - std::cout << "Time zone ID does not canonicalize to IANA: " << recovered_iana_id << std::endl; + std::string normalized_iana_id = mapper.normalize_iana("America/CHICAGO").ok().value(); + if (normalized_iana_id != "America/Chicago") { + std::cout << "Time zone ID does not normalize: " << normalized_iana_id << std::endl; + return 1; + } + std::string canonicalied_iana_id = mapper.canonicalize_iana("Asia/Calcutta").ok().value(); + if (canonicalied_iana_id != "Asia/Kolkata") { + std::cout << "Time zone ID does not canonicalize: " << canonicalied_iana_id << std::endl; + return 1; + } + std::string slow_recovered_iana_id = mapper.find_canonical_iana_from_bcp47("uschi").ok().value(); + if (slow_recovered_iana_id != "America/Chicago") { + std::cout << "Time zone ID does not roundtrip (slow): " << slow_recovered_iana_id << std::endl; + return 1; + } + ICU4XTimeZoneIdMapperWithFastCanonicalization reverse_mapper = ICU4XTimeZoneIdMapperWithFastCanonicalization::create(dp).ok().value(); + std::string fast_recovered_iana_id = reverse_mapper.canonical_iana_from_bcp47("uschi").ok().value(); + if (fast_recovered_iana_id != "America/Chicago") { + std::cout << "Time zone ID does not roundtrip (fast): " << fast_recovered_iana_id << std::endl; return 1; } ICU4XIsoDateTime local_datetime = ICU4XIsoDateTime::create(2022, 8, 25, 0, 0, 0, 0).ok().value(); From 4310f683678024e0605b97892ebd270bbf6f8e44 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 17:19:25 -0700 Subject: [PATCH 19/31] Update public docs and things --- CHANGELOG.md | 2 ++ components/datetime/src/time_zone.rs | 6 +++--- components/timezone/README.md | 8 ++++---- components/timezone/src/lib.rs | 8 ++++---- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9819180749d..c4504f8e1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ - Implement Joining_Type property (https://github.com/unicode-org/icu4x/pull/4599) - `icu_segmenter` - Fix Unicode 15.0 line breaking (https://github.com/unicode-org/icu4x/pull/4389) + - `icu_timezone` + - Added `TimeZoneIdMapper` to replace `IanaToBcp47Mapper` (https://github.com/unicode-org/icu4x/pull/4774) - Data model and providers - `icu_datagen` - Datagen shows elapsed time for keys that are slow to generate (https://github.com/unicode-org/icu4x/pull/4469) diff --git a/components/datetime/src/time_zone.rs b/components/datetime/src/time_zone.rs index cdc5053a23e..548544013ad 100644 --- a/components/datetime/src/time_zone.rs +++ b/components/datetime/src/time_zone.rs @@ -74,7 +74,7 @@ where /// /// ``` /// use icu::calendar::DateTime; -/// use icu::timezone::{CustomTimeZone, MetazoneCalculator, IanaToBcp47Mapper}; +/// use icu::timezone::{CustomTimeZone, MetazoneCalculator, TimeZoneIdMapper}; /// use icu::datetime::{DateTimeError, time_zone::TimeZoneFormatter}; /// use icu::locid::locale; /// use tinystr::tinystr; @@ -89,7 +89,7 @@ where /// // Set up the Metazone calculator, time zone ID mapper, /// // and the DateTime to use in calculation /// let mzc = MetazoneCalculator::new(); -/// let mapper = IanaToBcp47Mapper::new(); +/// let mapper = TimeZoneIdMapper::new(); /// let datetime = DateTime::try_new_iso_datetime(2022, 8, 29, 0, 0, 0) /// .unwrap(); /// @@ -104,7 +104,7 @@ where /// /// // "uschi" - has metazone symbol data for generic_non_location_short /// let mut time_zone = "-0600".parse::().unwrap(); -/// time_zone.time_zone_id = mapper.as_borrowed().get("America/Chicago"); +/// time_zone.time_zone_id = mapper.as_borrowed().iana_to_bcp47("America/Chicago"); /// time_zone.maybe_calculate_metazone(&mzc, &datetime); /// assert_writeable_eq!( /// tzf.format(&time_zone), diff --git a/components/timezone/README.md b/components/timezone/README.md index 2dd844c10ed..3400b630641 100644 --- a/components/timezone/README.md +++ b/components/timezone/README.md @@ -31,7 +31,7 @@ There are two mostly-interchangeable standards for time zone IDs: 2. BCP-47 time zone IDs, like `"uschi"` ICU4X uses BCP-47 time zone IDs for all of its APIs. To get a BCP-47 time zone from an -IANA time zone, use [`IanaToBcp47Mapper`]. +IANA time zone, use [`TimeZoneIdMapper`]. ### Metazone @@ -86,15 +86,15 @@ the metazone based on a certain local datetime: use icu::calendar::DateTime; use icu::timezone::CustomTimeZone; use icu::timezone::GmtOffset; -use icu::timezone::IanaToBcp47Mapper; +use icu::timezone::TimeZoneIdMapper; use icu::timezone::MetazoneCalculator; use tinystr::{tinystr, TinyAsciiStr}; // Create a time zone for America/Chicago at GMT-6: let mut time_zone = CustomTimeZone::new_empty(); time_zone.gmt_offset = "-0600".parse::().ok(); -let mapper = IanaToBcp47Mapper::new(); -time_zone.time_zone_id = mapper.as_borrowed().get("America/Chicago"); +let mapper = TimeZoneIdMapper::new(); +time_zone.time_zone_id = mapper.as_borrowed().iana_to_bcp47("America/Chicago"); // Alternatively, set it directly from the BCP-47 ID assert_eq!(time_zone.time_zone_id, Some(tinystr!(8, "uschi").into())); diff --git a/components/timezone/src/lib.rs b/components/timezone/src/lib.rs index 39abb1f481e..fee43c6bed5 100644 --- a/components/timezone/src/lib.rs +++ b/components/timezone/src/lib.rs @@ -31,7 +31,7 @@ //! 2. BCP-47 time zone IDs, like `"uschi"` //! //! ICU4X uses BCP-47 time zone IDs for all of its APIs. To get a BCP-47 time zone from an -//! IANA time zone, use [`IanaToBcp47Mapper`]. +//! IANA time zone, use [`TimeZoneIdMapper`]. //! //! ## Metazone //! @@ -86,15 +86,15 @@ //! use icu::calendar::DateTime; //! use icu::timezone::CustomTimeZone; //! use icu::timezone::GmtOffset; -//! use icu::timezone::IanaToBcp47Mapper; +//! use icu::timezone::TimeZoneIdMapper; //! use icu::timezone::MetazoneCalculator; //! use tinystr::{tinystr, TinyAsciiStr}; //! //! // Create a time zone for America/Chicago at GMT-6: //! let mut time_zone = CustomTimeZone::new_empty(); //! time_zone.gmt_offset = "-0600".parse::().ok(); -//! let mapper = IanaToBcp47Mapper::new(); -//! time_zone.time_zone_id = mapper.as_borrowed().get("America/Chicago"); +//! let mapper = TimeZoneIdMapper::new(); +//! time_zone.time_zone_id = mapper.as_borrowed().iana_to_bcp47("America/Chicago"); //! //! // Alternatively, set it directly from the BCP-47 ID //! assert_eq!(time_zone.time_zone_id, Some(tinystr!(8, "uschi").into())); From 7a59526b9db8aa8e36db19146d0cc2f56696ca41 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 17:33:44 -0700 Subject: [PATCH 20/31] features --- ffi/capi/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi/capi/src/lib.rs b/ffi/capi/src/lib.rs index b400160e390..5a144240147 100644 --- a/ffi/capi/src/lib.rs +++ b/ffi/capi/src/lib.rs @@ -151,7 +151,7 @@ pub mod time; pub mod timezone; #[cfg(feature = "icu_datetime")] pub mod timezone_formatter; -#[cfg(feature = "icu_timezone")] +#[cfg(any(feature = "icu_datetime", feature = "icu_timezone"))] pub mod timezone_mapper; #[cfg(feature = "icu_calendar")] pub mod week; From 8985f9f3236ca36f0ecf56e85659ebb538dc651c Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 15 Apr 2024 23:18:29 -0700 Subject: [PATCH 21/31] clippy, fmt --- components/timezone/src/iana_ids.rs | 8 ++-- components/timezone/src/ids.rs | 14 +++---- ffi/capi/src/timezone.rs | 6 ++- ffi/capi/src/timezone_mapper.rs | 62 +++++++++++++++++++---------- 4 files changed, 55 insertions(+), 35 deletions(-) diff --git a/components/timezone/src/iana_ids.rs b/components/timezone/src/iana_ids.rs index 152d2000943..a8cb1f31a14 100644 --- a/components/timezone/src/iana_ids.rs +++ b/components/timezone/src/iana_ids.rs @@ -38,7 +38,7 @@ use icu_provider::prelude::*; /// ); /// ``` #[derive(Debug)] -#[deprecated(since = "1.5.0", note="use `TimeZoneIdMapper` instead")] +#[deprecated(since = "1.5.0", note = "use `TimeZoneIdMapper` instead")] pub struct IanaToBcp47Mapper { data: DataPayload, } @@ -94,7 +94,7 @@ impl IanaToBcp47Mapper { /// A borrowed wrapper around IANA-to-BCP47 time zone data, returned by /// [`IanaToBcp47Mapper::as_borrowed()`]. More efficient to query. #[derive(Debug)] -#[deprecated(since = "1.5.0", note="use `TimeZoneIdMapper` instead")] +#[deprecated(since = "1.5.0", note = "use `TimeZoneIdMapper` instead")] pub struct IanaToBcp47MapperBorrowed<'a> { data: &'a IanaToBcp47MapV1<'a>, } @@ -164,7 +164,7 @@ impl<'a> IanaToBcp47MapperBorrowed<'a> { /// assert_eq!(iana_id, Some("Asia/Kolkata")) /// ``` #[derive(Debug)] -#[deprecated(since = "1.5.0", note="use `TimeZoneIdMapper` instead")] +#[deprecated(since = "1.5.0", note = "use `TimeZoneIdMapper` instead")] pub struct IanaBcp47RoundTripMapper { data1: DataPayload, data2: DataPayload, @@ -234,7 +234,7 @@ impl IanaBcp47RoundTripMapper { /// A borrowed wrapper around IANA-BCP47 time zone data, returned by /// [`IanaBcp47RoundTripMapper::as_borrowed()`]. More efficient to query. #[derive(Debug)] -#[deprecated(since = "1.5.0", note="use `TimeZoneIdMapper` instead")] +#[deprecated(since = "1.5.0", note = "use `TimeZoneIdMapper` instead")] pub struct IanaBcp47RoundTripMapperBorrowed<'a> { data1: &'a IanaToBcp47MapV1<'a>, data2: &'a Bcp47ToIanaMapV1<'a>, diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index c076e7edd9d..4973007d663 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -68,6 +68,7 @@ impl TimeZoneIdMapper { /// ✨ *Enabled with the `compiled_data` Cargo feature.* /// /// [📚 Help choosing a constructor](icu_provider::constructors) + #[allow(clippy::new_without_default)] // feature-gated constructor #[cfg(feature = "compiled_data")] pub fn new() -> Self { Self { @@ -185,10 +186,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// assert_eq!(mapper.normalize_iana("America/San_Francisco"), None); /// ``` pub fn normalize_iana<'s>(&self, iana_id: &'s str) -> Option> { - let Some((trie_value, string)) = self.iana_lookup_with_normalization(iana_id, |_| {}) - else { - return None; - }; + let (trie_value, string) = self.iana_lookup_with_normalization(iana_id, |_| {})?; let Some(bcp47_id) = self.data.bcp47_ids.get(trie_value.index()) else { debug_assert!(false, "index should be in range"); return None; @@ -231,11 +229,9 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { // nearby the input IANA name. This should improve lookup time since // most renames share the same prefix like "Asia" or "Europe". let mut stack = Vec::with_capacity(iana_id.len()); - let Some((trie_value, string)) = self.iana_lookup_with_normalization(iana_id, |cursor| { + let (trie_value, string) = self.iana_lookup_with_normalization(iana_id, |cursor| { stack.push((cursor.clone(), 0, 1)); - }) else { - return None; - }; + })?; let Some(bcp47_id) = self.data.bcp47_ids.get(trie_value.index()) else { debug_assert!(false, "index should be in range"); return None; @@ -426,6 +422,7 @@ impl TimeZoneIdMapperWithFastCanonicalization { /// ✨ *Enabled with the `compiled_data` Cargo feature.* /// /// [📚 Help choosing a constructor](icu_provider::constructors) + #[allow(clippy::new_without_default)] // feature-gated constructor #[cfg(feature = "compiled_data")] pub fn new() -> Self { const _: () = assert!( @@ -639,6 +636,7 @@ impl<'a> TimeZoneIdMapperWithFastCanonicalizationBorrowed<'a> { /// A wrapper around a syntax-normalized IANA time zone identifier string /// and its corresponding BCP-47 time zone identifier. #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub struct NormalizedIana<'s> { /// The syntax-normalized IANA time zone identifier string. pub string: Cow<'s, str>, diff --git a/ffi/capi/src/timezone.rs b/ffi/capi/src/timezone.rs index cc7b847118f..443493bb1cf 100644 --- a/ffi/capi/src/timezone.rs +++ b/ffi/capi/src/timezone.rs @@ -131,7 +131,11 @@ pub mod ffi { #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id, Struct, compact)] #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id::from_str, FnInStruct, hidden)] #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id::deref, FnInStruct, hidden)] - #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id::Target, AssociatedTypeInStruct, hidden)] + #[diplomat::rust_link( + icu::timezone::TimeZoneBcp47Id::Target, + AssociatedTypeInStruct, + hidden + )] pub fn try_set_time_zone_id(&mut self, id: &DiplomatStr) -> Result<(), ICU4XError> { self.0.time_zone_id = Some(icu_timezone::TimeZoneBcp47Id( tinystr::TinyAsciiStr::from_bytes(id)?, diff --git a/ffi/capi/src/timezone_mapper.rs b/ffi/capi/src/timezone_mapper.rs index 3fb0068bd2b..f42f48b7535 100644 --- a/ffi/capi/src/timezone_mapper.rs +++ b/ffi/capi/src/timezone_mapper.rs @@ -7,7 +7,9 @@ pub mod ffi { use crate::errors::ffi::ICU4XError; use crate::provider::ffi::ICU4XDataProvider; use alloc::boxed::Box; - use icu_timezone::{TimeZoneBcp47Id, TimeZoneIdMapper, TimeZoneIdMapperWithFastCanonicalization}; + use icu_timezone::{ + TimeZoneBcp47Id, TimeZoneIdMapper, TimeZoneIdMapperWithFastCanonicalization, + }; use tinystr::TinyAsciiStr; /// A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. @@ -34,10 +36,7 @@ pub mod ffi { )?))) } - #[diplomat::rust_link( - icu::timezone::TimeZoneIdMapperBorrowed::iana_to_bcp47, - FnInStruct - )] + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperBorrowed::iana_to_bcp47, FnInStruct)] #[diplomat::rust_link( icu::timezone::TimeZoneIdMapperBorrowed::iana_bytes_to_bcp47, FnInStruct, @@ -57,10 +56,7 @@ pub mod ffi { } } - #[diplomat::rust_link( - icu::timezone::TimeZoneIdMapperBorrowed::normalize_iana, - FnInStruct - )] + #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperBorrowed::normalize_iana, FnInStruct)] pub fn normalize_iana( &self, value: &DiplomatStr, @@ -117,30 +113,52 @@ pub mod ffi { } } - /// A mapper between IANA time zone identifiers and BCP-47 time zone identifiers. /// /// This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. /// It also supports normalizing and canonicalizing the IANA strings. #[diplomat::opaque] #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization, Struct)] - #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization::as_borrowed, FnInStruct, hidden)] - #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization::inner, FnInStruct, hidden)] - #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalizationBorrowed, Struct, hidden)] - #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalizationBorrowed::inner, FnInStruct, hidden)] - pub struct ICU4XTimeZoneIdMapperWithFastCanonicalization(pub TimeZoneIdMapperWithFastCanonicalization); + #[diplomat::rust_link( + icu::timezone::TimeZoneIdMapperWithFastCanonicalization::as_borrowed, + FnInStruct, + hidden + )] + #[diplomat::rust_link( + icu::timezone::TimeZoneIdMapperWithFastCanonicalization::inner, + FnInStruct, + hidden + )] + #[diplomat::rust_link( + icu::timezone::TimeZoneIdMapperWithFastCanonicalizationBorrowed, + Struct, + hidden + )] + #[diplomat::rust_link( + icu::timezone::TimeZoneIdMapperWithFastCanonicalizationBorrowed::inner, + FnInStruct, + hidden + )] + pub struct ICU4XTimeZoneIdMapperWithFastCanonicalization( + pub TimeZoneIdMapperWithFastCanonicalization, + ); impl ICU4XTimeZoneIdMapperWithFastCanonicalization { - #[diplomat::rust_link(icu::timezone::TimeZoneIdMapperWithFastCanonicalization::new, FnInStruct)] + #[diplomat::rust_link( + icu::timezone::TimeZoneIdMapperWithFastCanonicalization::new, + FnInStruct + )] pub fn create( provider: &ICU4XDataProvider, ) -> Result, ICU4XError> { - Ok(Box::new(ICU4XTimeZoneIdMapperWithFastCanonicalization(call_constructor!( - TimeZoneIdMapperWithFastCanonicalization::new [r => Ok(r)], - TimeZoneIdMapperWithFastCanonicalization::try_new_with_any_provider, - TimeZoneIdMapperWithFastCanonicalization::try_new_with_buffer_provider, - provider, - )?))) + Ok(Box::new(ICU4XTimeZoneIdMapperWithFastCanonicalization( + call_constructor!( + TimeZoneIdMapperWithFastCanonicalization::new [r => Ok(r)], + TimeZoneIdMapperWithFastCanonicalization::try_new_with_any_provider, + TimeZoneIdMapperWithFastCanonicalization::try_new_with_buffer_provider, + provider, + )?, + ))) } #[diplomat::rust_link( From 92f4e7ea5592d8cf4da9f8ffeaded9be35de8373 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 18 Apr 2024 15:17:10 -0700 Subject: [PATCH 22/31] Add constructor Diplomat attr --- ffi/capi/src/timezone_mapper.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ffi/capi/src/timezone_mapper.rs b/ffi/capi/src/timezone_mapper.rs index f42f48b7535..72225e2d56e 100644 --- a/ffi/capi/src/timezone_mapper.rs +++ b/ffi/capi/src/timezone_mapper.rs @@ -25,6 +25,7 @@ pub mod ffi { impl ICU4XTimeZoneIdMapper { #[diplomat::rust_link(icu::timezone::TimeZoneIdMapper::new, FnInStruct)] + #[diplomat::attr(all(supports = constructors, supports = fallible_constructors), constructor)] pub fn create( provider: &ICU4XDataProvider, ) -> Result, ICU4XError> { @@ -148,6 +149,7 @@ pub mod ffi { icu::timezone::TimeZoneIdMapperWithFastCanonicalization::new, FnInStruct )] + #[diplomat::attr(all(supports = constructors, supports = fallible_constructors), constructor)] pub fn create( provider: &ICU4XDataProvider, ) -> Result, ICU4XError> { From fe607703f92ba6ca464d7ee1c4f8b36f8d0935b1 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 18 Apr 2024 15:21:09 -0700 Subject: [PATCH 23/31] impl Default --- components/timezone/src/ids.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index 4973007d663..e2f0dcf0b9b 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -60,6 +60,13 @@ pub struct TimeZoneIdMapper { data: DataPayload, } +#[cfg(feature = "compiled_data")] +impl Default for TimeZoneIdMapper { + fn default() -> Self { + Self::new() + } +} + impl TimeZoneIdMapper { /// Creates a new [`TimeZoneIdMapper`] using compiled data. /// @@ -68,7 +75,6 @@ impl TimeZoneIdMapper { /// ✨ *Enabled with the `compiled_data` Cargo feature.* /// /// [📚 Help choosing a constructor](icu_provider::constructors) - #[allow(clippy::new_without_default)] // feature-gated constructor #[cfg(feature = "compiled_data")] pub fn new() -> Self { Self { @@ -414,6 +420,13 @@ pub struct TimeZoneIdMapperWithFastCanonicalization { data: DataPayload, } +#[cfg(feature = "compiled_data")] +impl Default for TimeZoneIdMapperWithFastCanonicalization { + fn default() -> Self { + Self::new() + } +} + impl TimeZoneIdMapperWithFastCanonicalization { /// Creates a new [`TimeZoneIdMapperWithFastCanonicalization`] using compiled data. /// @@ -422,7 +435,6 @@ impl TimeZoneIdMapperWithFastCanonicalization { /// ✨ *Enabled with the `compiled_data` Cargo feature.* /// /// [📚 Help choosing a constructor](icu_provider::constructors) - #[allow(clippy::new_without_default)] // feature-gated constructor #[cfg(feature = "compiled_data")] pub fn new() -> Self { const _: () = assert!( From c817256e36a81d6b99af965483a52a9d1111d395 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 18 Apr 2024 15:41:50 -0700 Subject: [PATCH 24/31] Document Normalization vs Canonicalization --- components/timezone/src/ids.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index e2f0dcf0b9b..21e1df91293 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -20,6 +20,29 @@ use crate::{ /// This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. /// It also supports normalizing and canonicalizing the IANA strings. /// +/// # Normalization vs Canonicalization +/// +/// Multiple IANA time zone identifiers can refer to the same BCP-47 time zone. For example, the +/// following three IANA identifiers all map to `"usind"`: +/// +/// - "America/Fort_Wayne" +/// - "America/Indiana/Indianapolis" +/// - "America/Indianapolis" +/// - "US/East-Indiana" +/// +/// There is only one canonical name, which is "America/Indiana/Indianapolis". The +/// *canonicalization* operation returns the canonical name. You should canonicalize if you +/// need to compare time zones for equality or display the name to the user. Note that the +/// canonical name can change over time. +/// +/// The *normalization* operation, on the other hand, keeps the input identifier but normalizes +/// the casing. For example, "AMERICA/FORT_WAYNE" normalizes to "America/Fort_Wayne". +/// Normalization is a data-driven operation because there are no algorithmic casing rules that +/// work for all IANA time zone identifiers. +/// +/// Normalization is a cheap operation, but canonicalization might be expensive. If you need +/// canonicalization that is reliably fast, use [`TimeZoneIdMapperWithFastCanonicalization`]. +/// /// # Examples /// /// ``` From 46921008bc071aaba7dd0959c87ade1017b52c72 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 18 Apr 2024 15:42:28 -0700 Subject: [PATCH 25/31] Line length --- components/timezone/src/ids.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index 21e1df91293..883db7e257c 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -285,9 +285,10 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// This function performs a slow linear search. If this is problematic, consider one of the /// following functions instead: /// - /// 1. [`TimeZoneIdMapperBorrowed::canonicalize_iana()`] is faster if you have an IANA ID. - /// 2. [`TimeZoneIdMapperWithFastCanonicalizationBorrowed::canonical_iana_from_bcp47()`] is faster, but it requires - /// loading additional data. + /// 1. [`TimeZoneIdMapperBorrowed::canonicalize_iana()`] + /// is faster if you have an IANA ID. + /// 2. [`TimeZoneIdMapperWithFastCanonicalizationBorrowed::canonical_iana_from_bcp47()`] + /// is faster, but it requires loading additional data. /// /// Returns `None` if the BCP-47 ID is not found. /// From 2647dcd164688aa38ba6d1926941960f4c74b5ad Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 18 Apr 2024 15:55:55 -0700 Subject: [PATCH 26/31] rm NormalizedIana --- components/timezone/src/ids.rs | 95 +++++++------------ components/timezone/src/lib.rs | 2 +- ffi/capi/src/timezone_mapper.rs | 10 +- .../src/transform/cldr/time_zones/names.rs | 4 +- 4 files changed, 43 insertions(+), 68 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index 883db7e257c..eaf4b99db08 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -74,7 +74,7 @@ use crate::{ /// mapper /// .canonicalize_iana("Australia/Victoria") /// .unwrap() -/// .string, +/// .0, /// "Australia/Melbourne" /// ); /// ``` @@ -202,25 +202,25 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// /// let result = mapper.normalize_iana("Asia/CALCUTTA").unwrap(); /// - /// assert_eq!(result.string, "Asia/Calcutta"); - /// assert!(matches!(result.string, Cow::Owned(_))); - /// assert_eq!(*result.bcp47_id, "inccu"); + /// assert_eq!(result.0, "Asia/Calcutta"); + /// assert!(matches!(result.0, Cow::Owned(_))); + /// assert_eq!(*result.1, "inccu"); /// /// // Borrows when able: /// let result = mapper.normalize_iana("America/Chicago").unwrap(); - /// assert_eq!(result.string, "America/Chicago"); - /// assert!(matches!(result.string, Cow::Borrowed(_))); + /// assert_eq!(result.0, "America/Chicago"); + /// assert!(matches!(result.0, Cow::Borrowed(_))); /// /// // Unknown IANA time zone ID: /// assert_eq!(mapper.normalize_iana("America/San_Francisco"), None); /// ``` - pub fn normalize_iana<'s>(&self, iana_id: &'s str) -> Option> { + pub fn normalize_iana<'s>(&self, iana_id: &'s str) -> Option<(Cow<'s, str>, TimeZoneBcp47Id)> { let (trie_value, string) = self.iana_lookup_with_normalization(iana_id, |_| {})?; let Some(bcp47_id) = self.data.bcp47_ids.get(trie_value.index()) else { debug_assert!(false, "index should be in range"); return None; }; - Some(NormalizedIana { string, bcp47_id }) + Some((string, bcp47_id)) } /// Returns the canonical, normalized name of the given IANA time zone. @@ -241,19 +241,19 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// /// let result = mapper.canonicalize_iana("Asia/CALCUTTA").unwrap(); /// - /// assert_eq!(result.string, "Asia/Kolkata"); - /// assert!(matches!(result.string, Cow::Owned(_))); - /// assert_eq!(*result.bcp47_id, "inccu"); + /// assert_eq!(result.0, "Asia/Kolkata"); + /// assert!(matches!(result.0, Cow::Owned(_))); + /// assert_eq!(*result.1, "inccu"); /// /// // Borrows when able: /// let result = mapper.canonicalize_iana("America/Chicago").unwrap(); - /// assert_eq!(result.string, "America/Chicago"); - /// assert!(matches!(result.string, Cow::Borrowed(_))); + /// assert_eq!(result.0, "America/Chicago"); + /// assert!(matches!(result.0, Cow::Borrowed(_))); /// /// // Unknown IANA time zone ID: /// assert_eq!(mapper.canonicalize_iana("America/San_Francisco"), None); /// ``` - pub fn canonicalize_iana<'s>(&self, iana_id: &'s str) -> Option> { + pub fn canonicalize_iana<'s>(&self, iana_id: &'s str) -> Option<(Cow<'s, str>, TimeZoneBcp47Id)> { // Note: We collect the cursors into a stack so that we start probing // nearby the input IANA name. This should improve lookup time since // most renames share the same prefix like "Asia" or "Europe". @@ -266,7 +266,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { return None; }; if trie_value.is_canonical() { - return Some(NormalizedIana { string, bcp47_id }); + return Some((string, bcp47_id)); } // If we get here, we need to walk the trie to find the canonical IANA ID. let needle = trie_value.to_canonical(); @@ -274,10 +274,9 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { debug_assert!(false, "every time zone should have a canonical IANA ID"); return None; }; - Some(NormalizedIana { - string: Cow::Owned(string), + Some((Cow::Owned(string), bcp47_id, - }) + )) } /// Returns the canonical, normalized IANA ID of the given BCP-47 ID. @@ -306,9 +305,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "inccu")); /// let result = mapper.find_canonical_iana_from_bcp47(bcp47_id).unwrap(); /// - /// assert_eq!(result.string, "Asia/Kolkata"); - /// assert!(matches!(result.string, Cow::Owned(_))); - /// assert_eq!(*result.bcp47_id, "inccu"); + /// assert_eq!(result, "Asia/Kolkata"); /// /// // Unknown BCP-47 time zone ID: /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "ussfo")); @@ -317,15 +314,12 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { pub fn find_canonical_iana_from_bcp47( &self, bcp47_id: TimeZoneBcp47Id, - ) -> Option> { + ) -> Option { let index = self.data.bcp47_ids.binary_search(&bcp47_id).ok()?; let stack = alloc::vec![(self.data.map.cursor(), 0, 0)]; let needle = IanaTrieValue::canonical_for_index(index); let string = self.iana_search(needle, String::new(), stack)?; - Some(NormalizedIana { - string: Cow::Owned(string), - bcp47_id, - }) + Some(string) } /// Queries the data for `iana_id` without recording the normalized string. @@ -604,20 +598,23 @@ impl<'a> TimeZoneIdMapperWithFastCanonicalizationBorrowed<'a> { /// let result = mapper.canonicalize_iana("Asia/CALCUTTA").unwrap(); /// /// // The Cow is always returned borrowed: - /// assert_eq!(result.string, "Asia/Kolkata"); - /// assert!(matches!(result.string, Cow::Borrowed(_))); - /// assert_eq!(*result.bcp47_id, "inccu"); + /// assert_eq!(result.0, "Asia/Kolkata"); + /// assert_eq!(*result.1, "inccu"); /// /// // Unknown IANA time zone ID: /// assert_eq!(mapper.canonicalize_iana("America/San_Francisco"), None); /// ``` - pub fn canonicalize_iana(&self, iana_id: &str) -> Option { + pub fn canonicalize_iana(&self, iana_id: &str) -> Option<(&str, TimeZoneBcp47Id)> { let trie_value = self.inner.iana_lookup_quick(iana_id)?; let Some(bcp47_id) = self.inner.data.bcp47_ids.get(trie_value.index()) else { debug_assert!(false, "index should be in range"); return None; }; - self.get_with_index(trie_value.index(), bcp47_id) + let Some(string) = self.data.canonical_iana_ids.get(trie_value.index()) else { + debug_assert!(false, "index should be in range"); + return None; + }; + Some((string, bcp47_id)) } /// Returns the canonical, normalized IANA ID of the given BCP-47 ID. @@ -642,44 +639,22 @@ impl<'a> TimeZoneIdMapperWithFastCanonicalizationBorrowed<'a> { /// let result = mapper.canonical_iana_from_bcp47(bcp47_id).unwrap(); /// /// // The Cow is always returned borrowed: - /// assert_eq!(result.string, "Asia/Kolkata"); - /// assert!(matches!(result.string, Cow::Borrowed(_))); - /// assert_eq!(*result.bcp47_id, "inccu"); + /// assert_eq!(result, "Asia/Kolkata"); /// /// // Unknown BCP-47 time zone ID: /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "ussfo")); /// assert_eq!(mapper.canonical_iana_from_bcp47(bcp47_id), None); /// ``` - pub fn canonical_iana_from_bcp47(&self, bcp47_id: TimeZoneBcp47Id) -> Option { + pub fn canonical_iana_from_bcp47(&self, bcp47_id: TimeZoneBcp47Id) -> Option<&str> { let index = self.inner.data.bcp47_ids.binary_search(&bcp47_id).ok()?; - self.get_with_index(index, bcp47_id) - } - - fn get_with_index(&self, index: usize, bcp47_id: TimeZoneBcp47Id) -> Option { - match self.data.canonical_iana_ids.get(index) { - Some(iana_id) => Some(NormalizedIana { - string: Cow::Borrowed(iana_id), - bcp47_id, - }), - None => { - debug_assert!(false, "index should be in range"); - None - } - } + let Some(string) = self.data.canonical_iana_ids.get(index) else { + debug_assert!(false, "index should be in range"); + return None; + }; + Some(string) } } -/// A wrapper around a syntax-normalized IANA time zone identifier string -/// and its corresponding BCP-47 time zone identifier. -#[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] -pub struct NormalizedIana<'s> { - /// The syntax-normalized IANA time zone identifier string. - pub string: Cow<'s, str>, - /// The corresponding BCP-47 time zone identifier. - pub bcp47_id: TimeZoneBcp47Id, -} - #[derive(Copy, Clone, PartialEq, Eq)] #[repr(transparent)] struct IanaTrieValue(usize); diff --git a/components/timezone/src/lib.rs b/components/timezone/src/lib.rs index fee43c6bed5..e3ff35853e0 100644 --- a/components/timezone/src/lib.rs +++ b/components/timezone/src/lib.rs @@ -140,7 +140,7 @@ pub use iana_ids::{ IanaToBcp47MapperBorrowed, }; pub use ids::{ - NormalizedIana, TimeZoneIdMapper, TimeZoneIdMapperBorrowed, + TimeZoneIdMapper, TimeZoneIdMapperBorrowed, TimeZoneIdMapperWithFastCanonicalization, TimeZoneIdMapperWithFastCanonicalizationBorrowed, }; pub use metazone::MetazoneCalculator; diff --git a/ffi/capi/src/timezone_mapper.rs b/ffi/capi/src/timezone_mapper.rs index 72225e2d56e..122251ef7bb 100644 --- a/ffi/capi/src/timezone_mapper.rs +++ b/ffi/capi/src/timezone_mapper.rs @@ -68,7 +68,7 @@ pub mod ffi { // Validate the UTF-8 here because it gets echoed back to the writeable let value = core::str::from_utf8(value)?; if let Some(s) = handle.normalize_iana(value) { - Ok(s.string.write_to(write)?) + Ok(s.0.write_to(write)?) } else { Err(ICU4XError::TimeZoneInvalidIdError) } @@ -88,7 +88,7 @@ pub mod ffi { // Validate the UTF-8 here because it gets echoed back to the writeable let value = core::str::from_utf8(value)?; if let Some(s) = handle.canonicalize_iana(value) { - Ok(s.string.write_to(write)?) + Ok(s.0.write_to(write)?) } else { Err(ICU4XError::TimeZoneInvalidIdError) } @@ -107,7 +107,7 @@ pub mod ffi { let handle = self.0.as_borrowed(); let bcp47_id = TimeZoneBcp47Id(TinyAsciiStr::from_bytes(value)?); if let Some(s) = handle.find_canonical_iana_from_bcp47(bcp47_id) { - Ok(s.string.write_to(write)?) + Ok(s.write_to(write)?) } else { Err(ICU4XError::TimeZoneInvalidIdError) } @@ -177,7 +177,7 @@ pub mod ffi { // Validate the UTF-8 here because it gets echoed back to the writeable let value = core::str::from_utf8(value)?; if let Some(s) = handle.canonicalize_iana(value) { - Ok(s.string.write_to(write)?) + Ok(s.0.write_to(write)?) } else { Err(ICU4XError::TimeZoneInvalidIdError) } @@ -196,7 +196,7 @@ pub mod ffi { let handle = self.0.as_borrowed(); let bcp47_id = TimeZoneBcp47Id(TinyAsciiStr::from_bytes(value)?); if let Some(s) = handle.canonical_iana_from_bcp47(bcp47_id) { - Ok(s.string.write_to(write)?) + Ok(s.write_to(write)?) } else { Err(ICU4XError::TimeZoneInvalidIdError) } diff --git a/provider/datagen/src/transform/cldr/time_zones/names.rs b/provider/datagen/src/transform/cldr/time_zones/names.rs index cfed5e4616d..1f79e9f3a4b 100644 --- a/provider/datagen/src/transform/cldr/time_zones/names.rs +++ b/provider/datagen/src/transform/cldr/time_zones/names.rs @@ -262,13 +262,13 @@ fn test_normalize_canonicalize_iana_coverage() { let mapper = mapper.as_borrowed(); for iana_id in iana2bcp.keys() { - let normalized = mapper.normalize_iana(&iana_id).unwrap().string; + let normalized = mapper.normalize_iana(&iana_id).unwrap().0; assert_eq!(&normalized, iana_id); } let bcp2iana = compute_canonical_tzids_btreemap(&resource.keyword.u.time_zones.values); for (iana_id, bcp47_id) in iana2bcp.iter() { - let canonicalized = mapper.canonicalize_iana(&iana_id).unwrap().string; + let canonicalized = mapper.canonicalize_iana(&iana_id).unwrap().0; assert_eq!(&canonicalized, bcp2iana.get(bcp47_id).unwrap()); } } From 4e9d5ee86fb558693589ab0403898b2ac9cb1099 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 18 Apr 2024 15:56:09 -0700 Subject: [PATCH 27/31] fmt --- components/timezone/src/ids.rs | 14 ++++++-------- components/timezone/src/lib.rs | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index eaf4b99db08..de15a6f32e2 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -253,7 +253,10 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// // Unknown IANA time zone ID: /// assert_eq!(mapper.canonicalize_iana("America/San_Francisco"), None); /// ``` - pub fn canonicalize_iana<'s>(&self, iana_id: &'s str) -> Option<(Cow<'s, str>, TimeZoneBcp47Id)> { + pub fn canonicalize_iana<'s>( + &self, + iana_id: &'s str, + ) -> Option<(Cow<'s, str>, TimeZoneBcp47Id)> { // Note: We collect the cursors into a stack so that we start probing // nearby the input IANA name. This should improve lookup time since // most renames share the same prefix like "Asia" or "Europe". @@ -274,9 +277,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { debug_assert!(false, "every time zone should have a canonical IANA ID"); return None; }; - Some((Cow::Owned(string), - bcp47_id, - )) + Some((Cow::Owned(string), bcp47_id)) } /// Returns the canonical, normalized IANA ID of the given BCP-47 ID. @@ -311,10 +312,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// let bcp47_id = TimeZoneBcp47Id(tinystr!(8, "ussfo")); /// assert_eq!(mapper.find_canonical_iana_from_bcp47(bcp47_id), None); /// ``` - pub fn find_canonical_iana_from_bcp47( - &self, - bcp47_id: TimeZoneBcp47Id, - ) -> Option { + pub fn find_canonical_iana_from_bcp47(&self, bcp47_id: TimeZoneBcp47Id) -> Option { let index = self.data.bcp47_ids.binary_search(&bcp47_id).ok()?; let stack = alloc::vec![(self.data.map.cursor(), 0, 0)]; let needle = IanaTrieValue::canonical_for_index(index); diff --git a/components/timezone/src/lib.rs b/components/timezone/src/lib.rs index e3ff35853e0..0aafe0cbe6a 100644 --- a/components/timezone/src/lib.rs +++ b/components/timezone/src/lib.rs @@ -140,8 +140,8 @@ pub use iana_ids::{ IanaToBcp47MapperBorrowed, }; pub use ids::{ - TimeZoneIdMapper, TimeZoneIdMapperBorrowed, - TimeZoneIdMapperWithFastCanonicalization, TimeZoneIdMapperWithFastCanonicalizationBorrowed, + TimeZoneIdMapper, TimeZoneIdMapperBorrowed, TimeZoneIdMapperWithFastCanonicalization, + TimeZoneIdMapperWithFastCanonicalizationBorrowed, }; pub use metazone::MetazoneCalculator; pub use provider::{MetazoneId, TimeZoneBcp47Id}; From 33015b8d5121504489bbbd1b0bc1a48dff08d57b Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 22 Apr 2024 19:16:11 -0700 Subject: [PATCH 28/31] Clippy --- provider/datagen/src/transform/cldr/time_zones/names.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/datagen/src/transform/cldr/time_zones/names.rs b/provider/datagen/src/transform/cldr/time_zones/names.rs index 1f79e9f3a4b..f22042a9fea 100644 --- a/provider/datagen/src/transform/cldr/time_zones/names.rs +++ b/provider/datagen/src/transform/cldr/time_zones/names.rs @@ -262,13 +262,13 @@ fn test_normalize_canonicalize_iana_coverage() { let mapper = mapper.as_borrowed(); for iana_id in iana2bcp.keys() { - let normalized = mapper.normalize_iana(&iana_id).unwrap().0; + let normalized = mapper.normalize_iana(iana_id).unwrap().0; assert_eq!(&normalized, iana_id); } let bcp2iana = compute_canonical_tzids_btreemap(&resource.keyword.u.time_zones.values); for (iana_id, bcp47_id) in iana2bcp.iter() { - let canonicalized = mapper.canonicalize_iana(&iana_id).unwrap().0; + let canonicalized = mapper.canonicalize_iana(iana_id).unwrap().0; assert_eq!(&canonicalized, bcp2iana.get(bcp47_id).unwrap()); } } From 50f5b1ed4d3c3cc2372998ac52dddb8462edbc5e Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Tue, 23 Apr 2024 20:50:30 -0500 Subject: [PATCH 29/31] Review feedback --- components/timezone/src/ids.rs | 47 +++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index de15a6f32e2..653a2c48856 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -20,6 +20,14 @@ use crate::{ /// This mapper supports two-way mapping, but it is optimized for the case of IANA to BCP-47. /// It also supports normalizing and canonicalizing the IANA strings. /// +/// There are approximately 600 IANA identifiers and 450 BCP-47 identifiers. +/// +/// BCP-47 time zone identifiers are 8 ASCII characters or less and currently +/// average 5.1 characters long. Current IANA time zone identifiers are less than +/// 40 ASCII characters and average 14.2 characters long. +/// +/// These lists grow very slowly; in a typical year, 2-3 new identifiers are added. +/// /// # Normalization vs Canonicalization /// /// Multiple IANA time zone identifiers can refer to the same BCP-47 time zone. For example, the @@ -30,17 +38,19 @@ use crate::{ /// - "America/Indianapolis" /// - "US/East-Indiana" /// -/// There is only one canonical name, which is "America/Indiana/Indianapolis". The -/// *canonicalization* operation returns the canonical name. You should canonicalize if you -/// need to compare time zones for equality or display the name to the user. Note that the -/// canonical name can change over time. +/// There is only one canonical identifier, which is "America/Indiana/Indianapolis". The +/// *canonicalization* operation returns the canonical identifier. You should canonicalize if +/// you need to compare time zones for equality. Note that the canonical identifier can change +/// over time. For example, the identifier "Europe/Kiev" was renamed to the newly-added +/// identifier "Europe/Kyiv" in 2022. /// /// The *normalization* operation, on the other hand, keeps the input identifier but normalizes /// the casing. For example, "AMERICA/FORT_WAYNE" normalizes to "America/Fort_Wayne". /// Normalization is a data-driven operation because there are no algorithmic casing rules that /// work for all IANA time zone identifiers. /// -/// Normalization is a cheap operation, but canonicalization might be expensive. If you need +/// Normalization is a cheap operation, but canonicalization might be expensive, since it might +/// require searching over all IANA IDs to find the canonicalization. If you need /// canonicalization that is reliably fast, use [`TimeZoneIdMapperWithFastCanonicalization`]. /// /// # Examples @@ -69,7 +79,7 @@ use crate::{ /// Some("aumel".parse().unwrap()) /// ); /// -/// // We can recover the canonical name from the mapper: +/// // We can recover the canonical identifier from the mapper: /// assert_eq!( /// mapper /// .canonicalize_iana("Australia/Victoria") @@ -223,7 +233,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { Some((string, bcp47_id)) } - /// Returns the canonical, normalized name of the given IANA time zone. + /// Returns the canonical, normalized identifier of the given IANA time zone. /// /// Also returns the BCP-47 time zone ID. /// @@ -258,7 +268,7 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { iana_id: &'s str, ) -> Option<(Cow<'s, str>, TimeZoneBcp47Id)> { // Note: We collect the cursors into a stack so that we start probing - // nearby the input IANA name. This should improve lookup time since + // nearby the input IANA identifier. This should improve lookup time since // most renames share the same prefix like "Asia" or "Europe". let mut stack = Vec::with_capacity(iana_id.len()); let (trie_value, string) = self.iana_lookup_with_normalization(iana_id, |cursor| { @@ -282,13 +292,14 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { /// Returns the canonical, normalized IANA ID of the given BCP-47 ID. /// - /// This function performs a slow linear search. If this is problematic, consider one of the + /// This function performs a linear search over all IANA IDs. If this is problematic, consider one of the /// following functions instead: /// /// 1. [`TimeZoneIdMapperBorrowed::canonicalize_iana()`] /// is faster if you have an IANA ID. /// 2. [`TimeZoneIdMapperWithFastCanonicalizationBorrowed::canonical_iana_from_bcp47()`] - /// is faster, but it requires loading additional data. + /// is faster, but it requires loading additional data + /// (see [`TimeZoneIdMapperWithFastCanonicalization`]). /// /// Returns `None` if the BCP-47 ID is not found. /// @@ -428,8 +439,12 @@ impl<'a> TimeZoneIdMapperBorrowed<'a> { } } -/// A mapper that supplements [`TimeZoneIdMapper`] with additional data to improve the performance -/// of canonical IANA ID lookup. +/// A mapper that supplements [`TimeZoneIdMapper`] with about 8 KB of additional data to +/// improve the performance of canonical IANA ID lookup. +/// +/// The data in [`TimeZoneIdMapper`] is optimized for IANA to BCP-47 lookup; the reverse +/// requires a linear walk over all ~600 IANA identifiers. The data added here allows for +/// constant-time mapping from BCP-47 to IANA. #[derive(Debug, Clone)] pub struct TimeZoneIdMapperWithFastCanonicalization { inner: I, @@ -574,12 +589,13 @@ impl<'a> TimeZoneIdMapperWithFastCanonicalizationBorrowed<'a> { self.inner } - /// Returns the canonical, normalized name of the given IANA time zone. + /// Returns the canonical, normalized identifier of the given IANA time zone. /// /// Also returns the BCP-47 time zone ID. /// /// This is a faster version of [`TimeZoneIdMapperBorrowed::canonicalize_iana()`] - /// and it always returns borrowed IANA strings, but it requires loading additional data. + /// and it always returns borrowed IANA strings, but it requires loading additional data + /// (see [`TimeZoneIdMapperWithFastCanonicalization`]). /// /// Returns `None` if the IANA ID is not found. /// @@ -618,7 +634,8 @@ impl<'a> TimeZoneIdMapperWithFastCanonicalizationBorrowed<'a> { /// Returns the canonical, normalized IANA ID of the given BCP-47 ID. /// /// This is a faster version of [`TimeZoneIdMapperBorrowed::find_canonical_iana_from_bcp47()`] - /// and it always returns borrowed IANA strings, but it requires loading additional data. + /// and it always returns borrowed IANA strings, but it requires loading additional data + /// (see [`TimeZoneIdMapperWithFastCanonicalization`]). /// /// Returns `None` if the BCP-47 ID is not found. /// From 63d5b8bd738e83d12dc13348b1238b0f39757814 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Wed, 24 Apr 2024 09:08:28 -0700 Subject: [PATCH 30/31] fmt --- components/timezone/src/ids.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/timezone/src/ids.rs b/components/timezone/src/ids.rs index 653a2c48856..c5bd6d6b533 100644 --- a/components/timezone/src/ids.rs +++ b/components/timezone/src/ids.rs @@ -22,7 +22,7 @@ use crate::{ /// /// There are approximately 600 IANA identifiers and 450 BCP-47 identifiers. /// -/// BCP-47 time zone identifiers are 8 ASCII characters or less and currently +/// BCP-47 time zone identifiers are 8 ASCII characters or less and currently /// average 5.1 characters long. Current IANA time zone identifiers are less than /// 40 ASCII characters and average 14.2 characters long. /// From c74ce4450f41024b6da026fb885db5854bab0a31 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Fri, 17 May 2024 10:27:48 -0700 Subject: [PATCH 31/31] datagen --- provider/baked/timezone/data/macros.rs | 2 ++ .../macros/time_zone_iana_to_bcp47_v2.rs.data | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/provider/baked/timezone/data/macros.rs b/provider/baked/timezone/data/macros.rs index 1813966606b..9baef87ea2b 100644 --- a/provider/baked/timezone/data/macros.rs +++ b/provider/baked/timezone/data/macros.rs @@ -44,6 +44,8 @@ pub use __impliterable_time_zone_iana_to_bcp47_v1 as impliterable_time_zone_iana mod time_zone_iana_to_bcp47_v2; #[doc(inline)] pub use __impl_time_zone_iana_to_bcp47_v2 as impl_time_zone_iana_to_bcp47_v2; +#[doc(inline)] +pub use __impliterable_time_zone_iana_to_bcp47_v2 as impliterable_time_zone_iana_to_bcp47_v2; #[macro_use] #[path = "macros/time_zone_metazone_period_v1.rs.data"] mod time_zone_metazone_period_v1; diff --git a/provider/baked/timezone/data/macros/time_zone_iana_to_bcp47_v2.rs.data b/provider/baked/timezone/data/macros/time_zone_iana_to_bcp47_v2.rs.data index 93e927c47cb..4fbca2aa6f8 100644 --- a/provider/baked/timezone/data/macros/time_zone_iana_to_bcp47_v2.rs.data +++ b/provider/baked/timezone/data/macros/time_zone_iana_to_bcp47_v2.rs.data @@ -25,3 +25,18 @@ macro_rules! __impl_time_zone_iana_to_bcp47_v2 { } }; } +/// Implement `IterableDataProvider` on the given struct using the data +/// hardcoded in this file. This allows the struct to be used with +/// `DatagenDriver` for this key. +#[doc(hidden)] +#[macro_export] +macro_rules! __impliterable_time_zone_iana_to_bcp47_v2 { + ($ provider : ty) => { + #[clippy::msrv = "1.67"] + impl icu_provider::datagen::IterableDataProvider for $provider { + fn supported_locales(&self) -> Result, icu_provider::DataError> { + Ok([icu_provider::DataLocale::default()].into()) + } + } + }; +}