#![allow(deprecated)]
use elsa::sync::FrozenMap;
use icu_provider::datagen::IterableDataProvider;
use icu_provider::prelude::*;
use source::{AbstractFs, SerdeCache};
use std::collections::HashSet;
use std::fmt::Debug;
use std::path::PathBuf;
use std::sync::Arc;
use transform::cldr::source::CldrCache;
#[path = "transform/mod.rs"]
mod transform;
mod source;
#[cfg(test)]
mod tests;
#[allow(clippy::exhaustive_structs)] #[derive(Debug, Clone)]
pub struct DatagenProvider {
#[doc(hidden)] pub source: SourceData,
}
macro_rules! cb {
($($marker:path = $path:literal,)+ #[experimental] $($emarker:path = $epath:literal,)+) => {
icu_provider::make_exportable_provider!(
DatagenProvider,
[
icu_provider::hello_world::HelloWorldV1Marker,
$(
$marker,
)+
$(
#[cfg(feature = "experimental_components")]
$emarker,
)+
]
);
}
}
crate::registry!(cb);
icu_provider::impl_data_provider_never_marker!(DatagenProvider);
impl DatagenProvider {
pub const LATEST_TESTED_CLDR_TAG: &'static str = "45.0.0";
pub const LATEST_TESTED_ICUEXPORT_TAG: &'static str = "icu4x/2024-05-16/75.x";
pub const LATEST_TESTED_SEGMENTER_LSTM_TAG: &'static str = "v0.1.0";
#[cfg(feature = "networking")]
pub fn new_latest_tested() -> Self {
static SINGLETON: once_cell::sync::OnceCell<DatagenProvider> =
once_cell::sync::OnceCell::new();
SINGLETON
.get_or_init(|| {
Self::new_custom()
.with_cldr_for_tag(Self::LATEST_TESTED_CLDR_TAG)
.with_icuexport_for_tag(Self::LATEST_TESTED_ICUEXPORT_TAG)
.with_segmenter_lstm_for_tag(Self::LATEST_TESTED_SEGMENTER_LSTM_TAG)
})
.clone()
}
pub fn new_custom() -> Self {
Self {
source: SourceData {
cldr_paths: None,
icuexport_paths: None,
segmenter_lstm_paths: None,
trie_type: Default::default(),
collation_han_database: Default::default(),
#[cfg(feature = "legacy_api")]
icuexport_dictionary_fallback: None,
#[cfg(feature = "legacy_api")]
collations: Default::default(),
supported_locales_cache: Default::default(),
},
}
}
pub fn with_cldr(self, root: PathBuf) -> Result<Self, DataError> {
Ok(Self {
source: SourceData {
cldr_paths: Some(Arc::new(CldrCache::from_serde_cache(SerdeCache::new(
AbstractFs::new(root)?,
)))),
..self.source
},
})
}
pub fn with_icuexport(self, root: PathBuf) -> Result<Self, DataError> {
Ok(Self {
source: SourceData {
icuexport_paths: Some(Arc::new(SerdeCache::new(AbstractFs::new(root)?))),
..self.source
},
})
}
pub fn with_segmenter_lstm(self, root: PathBuf) -> Result<Self, DataError> {
Ok(Self {
source: SourceData {
segmenter_lstm_paths: Some(Arc::new(SerdeCache::new(AbstractFs::new(root)?))),
..self.source
},
})
}
#[cfg(feature = "networking")]
pub fn with_cldr_for_tag(self, tag: &str) -> Self {
Self {
source: SourceData {
cldr_paths: Some(Arc::new(CldrCache::from_serde_cache(SerdeCache::new(AbstractFs::new_from_url(format!(
"https://github.com/unicode-org/cldr-json/releases/download/{tag}/cldr-{tag}-json-full.zip",
)))))),
..self.source
}
}
}
#[cfg(feature = "networking")]
pub fn with_icuexport_for_tag(self, mut tag: &str) -> Self {
if tag == "release-71-1" {
tag = "icu4x/2022-08-17/71.x";
}
Self {
source: SourceData {
icuexport_paths: Some(Arc::new(SerdeCache::new(AbstractFs::new_from_url(format!(
"https://github.com/unicode-org/icu/releases/download/{tag}/icuexportdata_{}.zip",
tag.replace('/', "-")
))))),
..self.source
}
}
}
#[cfg(feature = "networking")]
pub fn with_segmenter_lstm_for_tag(self, tag: &str) -> Self {
Self { source: SourceData {
segmenter_lstm_paths: Some(Arc::new(SerdeCache::new(AbstractFs::new_from_url(format!(
"https://github.com/unicode-org/lstm_word_segmentation/releases/download/{tag}/models.zip"
))))),
..self.source }
}
}
const MISSING_CLDR_ERROR: DataError = DataErrorKind::MissingSourceData.with_str_context("cldr");
const MISSING_ICUEXPORT_ERROR: DataError =
DataErrorKind::MissingSourceData.with_str_context("icuexport");
const MISSING_SEGMENTER_LSTM_ERROR: DataError =
DataErrorKind::MissingSourceData.with_str_context("segmenter");
pub fn is_missing_cldr_error(mut e: DataError) -> bool {
e.key = None;
e == Self::MISSING_CLDR_ERROR
}
pub fn is_missing_icuexport_error(mut e: DataError) -> bool {
e.key = None;
e == Self::MISSING_ICUEXPORT_ERROR
}
pub fn is_missing_segmenter_lstm_error(mut e: DataError) -> bool {
e.key = None;
e == Self::MISSING_SEGMENTER_LSTM_ERROR
}
fn cldr(&self) -> Result<&CldrCache, DataError> {
self.source
.cldr_paths
.as_deref()
.ok_or(Self::MISSING_CLDR_ERROR)
}
fn icuexport(&self) -> Result<&SerdeCache, DataError> {
self.source
.icuexport_paths
.as_deref()
.ok_or(Self::MISSING_ICUEXPORT_ERROR)
}
fn segmenter_lstm(&self) -> Result<&SerdeCache, DataError> {
self.source
.segmenter_lstm_paths
.as_deref()
.ok_or(Self::MISSING_SEGMENTER_LSTM_ERROR)
}
pub fn with_fast_tries(self) -> Self {
Self {
source: SourceData {
trie_type: TrieType::Fast,
..self.source
},
}
}
pub fn with_collation_han_database(self, collation_han_database: CollationHanDatabase) -> Self {
Self {
source: SourceData {
collation_han_database,
..self.source
},
}
}
fn trie_type(&self) -> TrieType {
self.source.trie_type
}
fn collation_han_database(&self) -> CollationHanDatabase {
self.source.collation_han_database
}
pub fn locales_for_coverage_levels(
&self,
levels: impl IntoIterator<Item = CoverageLevel>,
) -> Result<impl IntoIterator<Item = icu_locid::LanguageIdentifier>, DataError> {
self.cldr()?.locales(levels)
}
fn supported_locales_set<M>(&self) -> Result<&HashSet<DataLocale>, DataError>
where
M: KeyedDataMarker,
Self: IterableDataProviderInternal<M>,
{
#[allow(deprecated)] self.source
.supported_locales_cache
.insert_with(M::KEY, || Box::new(self.supported_locales_impl()))
.as_ref()
.map_err(|e| *e)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
#[non_exhaustive]
pub enum CollationHanDatabase {
#[serde(rename = "implicit")]
#[default]
Implicit,
#[serde(rename = "unihan")]
Unihan,
}
impl std::fmt::Display for CollationHanDatabase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
CollationHanDatabase::Implicit => write!(f, "implicithan"),
CollationHanDatabase::Unihan => write!(f, "unihan"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, Hash)]
#[non_exhaustive]
#[serde(rename_all = "camelCase")]
pub enum CoverageLevel {
Modern,
Moderate,
Basic,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
#[doc(hidden)]
#[non_exhaustive]
pub enum TrieType {
#[serde(rename = "fast")]
Fast,
#[serde(rename = "small")]
#[default]
Small,
}
impl std::fmt::Display for TrieType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
TrieType::Fast => write!(f, "fast"),
TrieType::Small => write!(f, "small"),
}
}
}
trait IterableDataProviderInternal<M: KeyedDataMarker>: DataProvider<M> {
fn supported_locales_impl(&self) -> Result<HashSet<DataLocale>, DataError>;
}
impl<M: KeyedDataMarker> IterableDataProvider<M> for DatagenProvider
where
DatagenProvider: IterableDataProviderInternal<M>,
{
fn supported_locales(&self) -> Result<Vec<DataLocale>, DataError> {
self.supported_locales_set()
.map(|v| v.iter().cloned().collect())
}
fn supports_locale(&self, locale: &DataLocale) -> Result<bool, DataError> {
self.supported_locales_set().map(|v| v.contains(locale))
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
#[deprecated(since = "1.3.0", note = "use `DatagenProvider`")]
pub struct SourceData {
cldr_paths: Option<Arc<CldrCache>>,
icuexport_paths: Option<Arc<SerdeCache>>,
segmenter_lstm_paths: Option<Arc<SerdeCache>>,
trie_type: TrieType,
collation_han_database: CollationHanDatabase,
#[cfg(feature = "legacy_api")]
icuexport_dictionary_fallback: Option<Arc<SerdeCache>>,
#[cfg(feature = "legacy_api")]
pub(crate) collations: Vec<String>,
#[allow(clippy::type_complexity)] supported_locales_cache: Arc<FrozenMap<DataKey, Box<Result<HashSet<DataLocale>, DataError>>>>,
}
#[cfg(feature = "legacy_api")]
impl Default for SourceData {
fn default() -> Self {
Self {
icuexport_dictionary_fallback: Some(Arc::new(SerdeCache::new(AbstractFs::Memory(
[
(
"segmenter/dictionary/cjdict.toml",
include_bytes!("../tests/data/icuexport/segmenter/dictionary/cjdict.toml").as_slice(),
),
(
"segmenter/dictionary/khmerdict.toml",
include_bytes!("../tests/data/icuexport/segmenter/dictionary/khmerdict.toml").as_slice(),
),
(
"segmenter/dictionary/laodict.toml",
include_bytes!("../tests/data/icuexport/segmenter/dictionary/laodict.toml").as_slice(),
),
(
"segmenter/dictionary/burmesedict.toml",
include_bytes!("../tests/data/icuexport/segmenter/dictionary/burmesedict.toml").as_slice(),
),
(
"segmenter/dictionary/thaidict.toml",
include_bytes!("../tests/data/icuexport/segmenter/dictionary/thaidict.toml").as_slice(),
),
]
.into_iter()
.collect(),
)))),
segmenter_lstm_paths: Some(Arc::new(SerdeCache::new(AbstractFs::Memory(
[
(
"Khmer_codepoints_exclusive_model4_heavy/weights.json",
include_bytes!(
"../tests/data/lstm/Khmer_codepoints_exclusive_model4_heavy/weights.json"
)
.as_slice(),
),
(
"Lao_codepoints_exclusive_model4_heavy/weights.json",
include_bytes!(
"../tests/data/lstm/Lao_codepoints_exclusive_model4_heavy/weights.json"
)
.as_slice(),
),
(
"Burmese_codepoints_exclusive_model4_heavy/weights.json",
include_bytes!(
"../tests/data/lstm/Burmese_codepoints_exclusive_model4_heavy/weights.json"
)
.as_slice(),
),
(
"Thai_codepoints_exclusive_model4_heavy/weights.json",
include_bytes!(
"../tests/data/lstm/Thai_codepoints_exclusive_model4_heavy/weights.json"
)
.as_slice(),
),
(
"Thai_graphclust_model4_heavy/weights.json",
include_bytes!("../tests/data/lstm/Thai_graphclust_model4_heavy/weights.json")
.as_slice(),
),
]
.into_iter()
.collect(),
)))),
..DatagenProvider::new_custom().source
}
}
}
#[cfg(feature = "legacy_api")]
impl SourceData {
pub const LATEST_TESTED_CLDR_TAG: &'static str = DatagenProvider::LATEST_TESTED_CLDR_TAG;
pub const LATEST_TESTED_ICUEXPORT_TAG: &'static str =
DatagenProvider::LATEST_TESTED_ICUEXPORT_TAG;
#[cfg(feature = "networking")]
pub fn latest_tested() -> Self {
DatagenProvider::new_latest_tested().source
}
pub fn with_cldr(
self,
root: PathBuf,
_use_default_here: CldrLocaleSubset,
) -> Result<Self, DataError> {
Ok(DatagenProvider { source: self }.with_cldr(root)?.source)
}
pub fn with_icuexport(self, root: PathBuf) -> Result<Self, DataError> {
Ok(DatagenProvider { source: self }
.with_icuexport(root)?
.source)
}
#[cfg(feature = "networking")]
pub fn with_cldr_for_tag(
self,
tag: &str,
_use_default_here: CldrLocaleSubset,
) -> Result<Self, DataError> {
Ok(DatagenProvider { source: self }
.with_cldr_for_tag(tag)
.source)
}
#[cfg(feature = "networking")]
pub fn with_icuexport_for_tag(self, tag: &str) -> Result<Self, DataError> {
Ok(DatagenProvider { source: self }
.with_icuexport_for_tag(tag)
.source)
}
#[deprecated(
since = "1.1.0",
note = "Use `DatagenProvider::with_cldr_for_tag(DatagenProvider::LATEST_TESTED_CLDR_TAG)`"
)]
#[cfg(feature = "networking")]
pub fn with_cldr_latest(self, _use_default_here: CldrLocaleSubset) -> Result<Self, DataError> {
self.with_cldr_for_tag(Self::LATEST_TESTED_CLDR_TAG, Default::default())
}
#[deprecated(
since = "1.1.0",
note = "Use `DatagenProvider::with_icuexport_for_tag(DatagenProvider::LATEST_TESTED_ICUEXPORT_TAG)`"
)]
#[cfg(feature = "networking")]
pub fn with_icuexport_latest(self) -> Result<Self, DataError> {
self.with_icuexport_for_tag(Self::LATEST_TESTED_ICUEXPORT_TAG)
}
pub fn with_fast_tries(self) -> Self {
DatagenProvider { source: self }.with_fast_tries().source
}
pub fn with_collation_han_database(self, collation_han_database: CollationHanDatabase) -> Self {
DatagenProvider { source: self }
.with_collation_han_database(collation_han_database)
.source
}
#[cfg(feature = "legacy_api")]
pub fn with_collations(self, collations: Vec<String>) -> Self {
Self { collations, ..self }
}
pub fn locales(
&self,
levels: &[CoverageLevel],
) -> Result<Vec<icu_locid::LanguageIdentifier>, DataError> {
self.cldr_paths
.as_deref()
.ok_or(DatagenProvider::MISSING_CLDR_ERROR)?
.locales(levels.iter().copied())
}
}
#[allow(clippy::exhaustive_enums)] #[doc(hidden)]
#[derive(Debug)]
#[cfg(feature = "legacy_api")]
pub enum CldrLocaleSubset {
Ignored,
}
#[cfg(feature = "legacy_api")]
impl Default for CldrLocaleSubset {
fn default() -> Self {
Self::Ignored
}
}
#[cfg(feature = "legacy_api")]
impl CldrLocaleSubset {
#[allow(non_upper_case_globals)]
pub const Full: Self = Self::Ignored;
#[allow(non_upper_case_globals)]
pub const Modern: Self = Self::Ignored;
}
#[cfg(feature = "legacy_api")]
impl AnyProvider for DatagenProvider {
fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
self.as_any_provider().load_any(key, req)
}
}