use std::collections::HashMap;
use unic_langid::LanguageIdentifier;
#[cfg(not(feature = "cldr"))]
mod likely_subtags;
#[cfg(not(feature = "cldr"))]
use likely_subtags::MockLikelySubtags;
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum NegotiationStrategy {
Filtering,
Matching,
Lookup,
}
pub fn filter_matches<'a, R: 'a + AsRef<LanguageIdentifier>, A: 'a + AsRef<LanguageIdentifier>>(
requested: &[R],
available: &'a [A],
strategy: NegotiationStrategy,
) -> Vec<&'a A> {
let mut supported_locales = vec![];
let mut av_map: HashMap<&'a LanguageIdentifier, &'a A> = HashMap::new();
for av in available.iter() {
av_map.insert(av.as_ref(), av);
}
for req in requested {
let mut req = req.as_ref().to_owned();
if req.get_language() == "und" {
continue;
}
let mut match_found = false;
av_map.retain(|key, value| {
if strategy != NegotiationStrategy::Filtering && match_found {
return true;
}
if key.matches(&req, false, false) {
match_found = true;
supported_locales.push(*value);
return false;
}
true
});
if match_found {
match strategy {
NegotiationStrategy::Filtering => {}
NegotiationStrategy::Matching => continue,
NegotiationStrategy::Lookup => break,
}
}
match_found = false;
av_map.retain(|key, value| {
if strategy != NegotiationStrategy::Filtering && match_found {
return true;
}
if key.matches(&req, true, false) {
match_found = true;
supported_locales.push(*value);
return false;
}
true
});
if match_found {
match strategy {
NegotiationStrategy::Filtering => {}
NegotiationStrategy::Matching => continue,
NegotiationStrategy::Lookup => break,
};
}
match_found = false;
if req.add_likely_subtags() {
av_map.retain(|key, value| {
if strategy != NegotiationStrategy::Filtering && match_found {
return true;
}
if key.matches(&req, true, false) {
match_found = true;
supported_locales.push(*value);
return false;
}
true
});
if match_found {
match strategy {
NegotiationStrategy::Filtering => {}
NegotiationStrategy::Matching => continue,
NegotiationStrategy::Lookup => break,
};
}
match_found = false;
};
req.set_variants(&[]).unwrap();
av_map.retain(|key, value| {
if strategy != NegotiationStrategy::Filtering && match_found {
return true;
}
if key.matches(&req, true, true) {
match_found = true;
supported_locales.push(*value);
return false;
}
true
});
if match_found {
match strategy {
NegotiationStrategy::Filtering => {}
NegotiationStrategy::Matching => continue,
NegotiationStrategy::Lookup => break,
};
}
match_found = false;
req.set_region(None).unwrap();
if req.add_likely_subtags() {
av_map.retain(|key, value| {
if strategy != NegotiationStrategy::Filtering && match_found {
return true;
}
if key.matches(&req, true, false) {
match_found = true;
supported_locales.push(*value);
return false;
}
true
});
if match_found {
match strategy {
NegotiationStrategy::Filtering => {}
NegotiationStrategy::Matching => continue,
NegotiationStrategy::Lookup => break,
};
}
match_found = false;
}
req.set_region(None).unwrap();
av_map.retain(|key, value| {
if strategy != NegotiationStrategy::Filtering && match_found {
return true;
}
if key.matches(&req, true, true) {
match_found = true;
supported_locales.push(*value);
return false;
}
true
});
if match_found {
match strategy {
NegotiationStrategy::Filtering => {}
NegotiationStrategy::Matching => continue,
NegotiationStrategy::Lookup => break,
};
}
}
supported_locales
}
pub fn negotiate_languages<
'a,
R: 'a + AsRef<LanguageIdentifier>,
A: 'a + AsRef<LanguageIdentifier>,
>(
requested: &[R],
available: &'a [A],
default: Option<&'a A>,
strategy: NegotiationStrategy,
) -> Vec<&'a A> {
let mut supported = filter_matches(requested, available, strategy);
if let Some(default) = default {
if strategy == NegotiationStrategy::Lookup {
if supported.is_empty() {
supported.push(default);
}
} else if !supported.iter().any(|s| s.as_ref() == default.as_ref()) {
supported.push(default);
}
}
supported
}