I'm working on a crate called fluent-locale
which is meant to provide language negotiation capabilities.
I'm looking for advice from the community on how to design an API for this capability.
In the simplest form, language negotiation takes a list of language identifiers, filters and reorders them according to some criteria.
For example, it may look like this:
let available = ["en-US", "de-DE", "es-AR"];
let requested = ["de", "fr", "en"];
let supported = negotiate_languages(available, requested);
The list of supported
is coming from available
filtered out and reordered based on data from requested
.
The first issue is that since my functionality is really similar to drain
or filter
, I'm wondering if it should be some trait on Iterator, Array or Vec instead. In particular, I'd like to avoid cloning, but I'm not sure how to do it.
Second issue is that behind the scenes, I will not operate on strings. I'll operate on structs called LanguageIdentifier
(unic-langid
) which have TryFrom<&str>
.
If my API accepts &str
, then I have to decide what to do if TryFrom
fails - in other words, if someone passes a list of locales, and one of the locales is not valid, I'll need to decide how to handle that scenario within my API.
So it's tempting to instead ask users to create lists of LanguageIdentifier
and then negotiate them against lists of other LanguageIdentifier
.
It would be a bit less easy to work with, because in most cases, apps will receive lists of strings, either from the user, or from some settings. But the benefit of having the user decide how to handle incorrect strings, may make it worth it not to accept strings in my API. What do you think?
If I'm right, then the API could be a trait on Vec<LanguageIdentifier>
, and the user would do sth like:
let requested = req_strings.map(|s| LanguageIdentifier::try_from(s).unwrap()).collect();
let available = av_strings.map(|s| LanguageIdentifier::try_from(s).unwrap()).collect();
let supported = available.negotiate_against(requested);
Does it sound reasonable? What's the idiomatic way to avoid cloning when not necessary? Should I consume available
? Should I operate on &LanguageIdentifier
and clone the reference to supported
and keep available
?
Any ideas on how to design such API appreciated!
Hope my brainstorm questions make sense