#![crate_name = "locale"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
extern crate libc;
use std::fmt::Display;
use std::io::Result;
pub trait LocaleFactory {
fn get_numeric(&mut self) -> Option<Box<Numeric>> { None }
fn get_time(&mut self) -> Option<Box<Time>> { None }
}
#[derive(Debug, Clone)]
pub struct CompositeLocaleFactory<First: LocaleFactory, Second: LocaleFactory> {
first: First,
second: Second,
}
impl<F: LocaleFactory, S: LocaleFactory> CompositeLocaleFactory<F, S> {
pub fn new(first: F, second: S) -> Self {
CompositeLocaleFactory::<F, S> {
first: first, second: second
}
}
}
impl<F: LocaleFactory, S: LocaleFactory> LocaleFactory for CompositeLocaleFactory<F, S> {
fn get_numeric(&mut self) -> Option<Box<Numeric>> {
if let Some(v) = self.first.get_numeric() {
Some(v)
} else {
self.second.get_numeric()
}
}
fn get_time(&mut self) -> Option<Box<Time>> {
if let Some(v) = self.first.get_time() {
Some(v)
} else {
self.second.get_time()
}
}
}
#[derive(Debug, Clone, Default)]
pub struct InvariantLocaleFactory;
impl InvariantLocaleFactory {
#[allow(unused_variables)]
pub fn new(locale: &str) -> Result<Self> {
Ok(InvariantLocaleFactory)
}
}
impl LocaleFactory for InvariantLocaleFactory {
}
#[cfg(target_os = "linux")]
pub mod linux;
#[cfg(target_os = "linux")]
pub use linux::LibCLocaleFactory as SystemLocaleFactory;
pub mod macos;
#[cfg(target_os = "macos")]
pub use macos::MacOSLocaleFactory as SystemLocaleFactory;
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
pub use InvariantLocaleFactory as SystemLocaleFactory;
pub fn user_locale_factory() -> SystemLocaleFactory {
SystemLocaleFactory::new("").unwrap()
}
#[derive(Debug, Clone)]
pub struct Numeric {
pub decimal_sep: String,
pub thousands_sep: String,
}
impl Numeric {
pub fn load_user_locale() -> Result<Numeric> {
if let Ok(mut factory) = SystemLocaleFactory::new("") {
if let Some(numeric) = factory.get_numeric() {
return Ok(*numeric);
}
}
Ok(Numeric::english())
}
pub fn english() -> Numeric {
Numeric::new(".", ",")
}
pub fn new(decimal_sep: &str, thousands_sep: &str) -> Numeric {
Numeric {
decimal_sep: decimal_sep.to_string(),
thousands_sep: thousands_sep.to_string(),
}
}
pub fn format_int<I: Display>(&self, input: I) -> String {
let s = input.to_string();
let mut buf = String::new();
for (i, c) in s.chars().enumerate() {
buf.push(c);
if (s.len() - i - 1) % 3 == 0 && i != s.len() - 1 {
buf.push_str(&self.thousands_sep[..]);
}
}
buf
}
pub fn format_float<F: Display>(&self, input: F, decimal_places: usize) -> String {
format!("{:.*}", decimal_places, input).replace(".", &self.decimal_sep)
}
}
#[derive(Debug, Clone)]
pub struct Time {
month_names: Vec<String>,
long_month_names: Vec<String>,
day_names: Vec<String>,
long_day_names: Vec<String>,
}
impl Time {
pub fn load_user_locale() -> Result<Time> {
if let Ok(mut factory) = SystemLocaleFactory::new("") {
if let Some(time) = factory.get_time() {
return Ok(*time);
}
}
Ok(Time::english())
}
pub fn english() -> Time {
Time {
month_names: vec![
"Jan".to_string(), "Feb".to_string(), "Mar".to_string(),
"Apr".to_string(), "May".to_string(), "Jun".to_string(),
"Jul".to_string(), "Aug".to_string(), "Sep".to_string(),
"Oct".to_string(), "Nov".to_string(), "Dec".to_string(),
],
long_month_names: vec![
"January".to_string(), "February".to_string(),
"March".to_string(), "April".to_string(),
"May".to_string(), "June".to_string(),
"July".to_string(), "August".to_string(),
"September".to_string(), "October".to_string(),
"November".to_string(), "December".to_string(),
],
day_names: vec![
"Sun".to_string(),
"Mon".to_string(), "Tue".to_string(), "Wed".to_string(),
"Thu".to_string(), "Fri".to_string(), "Sat".to_string(),
],
long_day_names: vec![
"Sunday".to_string(),
"Monday".to_string(), "Tuesday".to_string(), "Wednesday".to_string(),
"Thursday".to_string(), "Friday".to_string(), "Saturday".to_string(),
],
}
}
pub fn long_month_name(&self, months_from_january: usize) -> String {
self.long_month_names[months_from_january].clone()
}
pub fn short_month_name(&self, months_from_january: usize) -> String {
self.month_names[months_from_january].clone()
}
pub fn long_day_name(&self, days_from_sunday: usize) -> String {
self.day_names[days_from_sunday].clone()
}
pub fn short_day_name(&self, days_from_sunday: usize) -> String {
self.day_names[days_from_sunday].clone()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn thousands_separator() {
let numeric_options = Numeric::new("/", "=");
assert_eq!("1=234=567".to_string(), numeric_options.format_int(1234567))
}
#[test]
fn thousands_separator_2() {
let numeric_options = Numeric::new("/", "=");
assert_eq!("123=456".to_string(), numeric_options.format_int(123456))
}
#[test]
fn thousands_separator_3() {
let numeric_options = Numeric::new("/", "=");
assert_eq!("12=345=678".to_string(), numeric_options.format_int(12345678))
}
}