diff --git a/lib/cldr_collation/nif.ex b/lib/cldr_collation/nif.ex index 332695a..cabe647 100644 --- a/lib/cldr_collation/nif.ex +++ b/lib/cldr_collation/nif.ex @@ -3,4 +3,6 @@ defmodule Cldr.Collation.Nif do otp_app: :ex_cldr_collation def sort(_locale, _list, _opts), do: :erlang.nif_error(:nif_not_loaded) + def sort_using_collator(_collator, _list), do: :erlang.nif_error(:nif_not_loaded) + def create_collator(_locale, _opts), do: :erlang.nif_error(:nif_not_loaded) end diff --git a/native/ex_cldr_collation/Cargo.lock b/native/ex_cldr_collation/Cargo.lock index 80daead..601ffce 100644 --- a/native/ex_cldr_collation/Cargo.lock +++ b/native/ex_cldr_collation/Cargo.lock @@ -34,6 +34,7 @@ version = "0.1.0" dependencies = [ "icu_collator", "icu_locid", + "icu_provider", "icu_testdata", "rustler", ] diff --git a/native/ex_cldr_collation/Cargo.toml b/native/ex_cldr_collation/Cargo.toml index 06f3c3b..b0f9233 100644 --- a/native/ex_cldr_collation/Cargo.toml +++ b/native/ex_cldr_collation/Cargo.toml @@ -11,5 +11,6 @@ crate-type = ["cdylib"] [dependencies] icu_collator = "1.2.0" icu_locid = "1.2.0" +icu_provider = { version = "1.2.0", features = ["sync"] } icu_testdata = "1.2.0" rustler = "0.28.0" diff --git a/native/ex_cldr_collation/src/collator.rs b/native/ex_cldr_collation/src/collator.rs new file mode 100644 index 0000000..a9dda55 --- /dev/null +++ b/native/ex_cldr_collation/src/collator.rs @@ -0,0 +1,17 @@ +use icu_collator::Collator; +use icu_locid::Locale; +use rustler::NifResult; + +fn parse_locale(locale_tag: &str) -> NifResult { + locale_tag.parse().map_err(|_| rustler::Error::BadArg) +} + +pub fn new( + locale_tag: &str, + opts: impl Into, +) -> NifResult { + let locale: Locale = parse_locale(locale_tag)?; + let collator = + Collator::try_new_unstable(&icu_testdata::unstable(), &locale.into(), opts.into()).unwrap(); + Ok(collator) +} diff --git a/native/ex_cldr_collation/src/collator_opts.rs b/native/ex_cldr_collation/src/collator_opts.rs index fde6017..bb7cef7 100644 --- a/native/ex_cldr_collation/src/collator_opts.rs +++ b/native/ex_cldr_collation/src/collator_opts.rs @@ -3,7 +3,6 @@ /// on its fields being stable. /// Thus we need to create our own version of CollatorOptions that we can use in our NIF API, and can be easily converted to icu_collator::ComparisonOptions. /// This also allows us the flexibily to adjust the NIF API if we so choose, while still being compatible with icu_collator. - use rustler::{NifMap, NifUnitEnum}; #[derive(NifUnitEnum)] @@ -149,4 +148,3 @@ impl From for icu_collator::CollatorOptions { collator_options } } - diff --git a/native/ex_cldr_collation/src/lib.rs b/native/ex_cldr_collation/src/lib.rs index 023348b..6eceb39 100644 --- a/native/ex_cldr_collation/src/lib.rs +++ b/native/ex_cldr_collation/src/lib.rs @@ -1,9 +1,16 @@ +mod collator; mod collator_opts; +mod resource; -use icu_collator::*; -use icu_locid::Locale; use collator_opts::CollatorOptions; -use rustler::NifResult; +use resource::{CollatorResource, CollatorResourceArc}; +use rustler::{Env, NifResult}; + +#[rustler::nif] +fn create_collator(locale_tag: &str, opts: CollatorOptions) -> NifResult { + let collator = collator::new(locale_tag, opts)?; + Ok(CollatorResource::new_arc(collator)) +} #[rustler::nif] fn sort<'a>( @@ -11,13 +18,28 @@ fn sort<'a>( list: Vec<&'a str>, opts: CollatorOptions, ) -> NifResult> { - let locale: Locale = locale_tag.parse().map_err(|_| rustler::Error::BadArg)?; - let collator = - Collator::try_new_unstable(&icu_testdata::unstable(), &locale.into(), opts.into()).unwrap(); + let collator = collator::new(locale_tag, opts)?; let mut list = list; list.sort_by(|first, second| collator.compare(first, second)); Ok(list) } -rustler::init!("Elixir.Cldr.Collation.Nif", [sort]); +#[rustler::nif] +fn sort_using_collator(collator_arc: CollatorResourceArc, list: Vec<&str>) -> Vec<&str> { + let collator = collator_arc.collator().lock().unwrap(); + let mut list = list; + list.sort_by(|first, second| collator.compare(first, second)); + list +} + +fn load(env: Env, _info: rustler::Term) -> bool { + rustler::resource!(CollatorResource, env); + true +} + +rustler::init!( + "Elixir.Cldr.Collation.Nif", + [sort, create_collator, sort_using_collator], + load = load +); diff --git a/native/ex_cldr_collation/src/resource.rs b/native/ex_cldr_collation/src/resource.rs new file mode 100644 index 0000000..569cfea --- /dev/null +++ b/native/ex_cldr_collation/src/resource.rs @@ -0,0 +1,25 @@ +use icu_collator::Collator; +use rustler::ResourceArc; +use std::sync::Mutex; + +pub struct CollatorResource { + collator: Mutex, +} + +impl CollatorResource { + pub fn new(collator: Collator) -> Self { + Self { + collator: Mutex::new(collator), + } + } + + pub fn new_arc(collator: Collator) -> ResourceArc { + ResourceArc::new(Self::new(collator)) + } + + pub fn collator(&self) -> &Mutex { + &self.collator + } +} + +pub type CollatorResourceArc = ResourceArc;