/*! Locale-specific functions. * * This file is intended as a library: * it must pass errors upwards * and panicking is allowed only when * this code encounters an internal inconsistency. */ use std::cmp; use std::ffi::{ CStr, CString }; use std::fmt; use std::os::raw::c_char; use std::ptr; use std::str::Utf8Error; mod c { use super::*; use std::os::raw::c_void; #[allow(non_camel_case_types)] pub type c_int = i32; #[derive(Clone, Copy)] #[repr(C)] pub struct GnomeXkbInfo(*const c_void); #[no_mangle] extern "C" { // from libc pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int; // from gnome-desktop3 pub fn gnome_xkb_info_new() -> GnomeXkbInfo; pub fn gnome_xkb_info_get_layout_info ( info: GnomeXkbInfo, id: *const c_char, display_name: *mut *const c_char, short_name: *const *const c_char, xkb_layout: *const *const c_char, xkb_variant: *const *const c_char ) -> c_int; pub fn g_object_unref(o: GnomeXkbInfo); } } #[derive(Debug)] pub enum Error { StringConversion(Utf8Error), NoInfo, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self, f) } } pub struct XkbInfo(c::GnomeXkbInfo); impl XkbInfo { pub fn new() -> XkbInfo { XkbInfo(unsafe { c::gnome_xkb_info_new() }) } pub fn get_display_name(&self, id: &str) -> Result { let id = cstring_safe(id); let id_ref = id.as_ptr(); let mut display_name: *const c_char = ptr::null(); let found = unsafe { c::gnome_xkb_info_get_layout_info( self.0, id_ref, &mut display_name as *mut *const c_char, ptr::null(), ptr::null(), ptr::null(), ) }; if found != 0 && !display_name.is_null() { let display_name = unsafe { CStr::from_ptr(display_name) }; display_name.to_str() .map(str::to_string) .map_err(Error::StringConversion) } else { Err(Error::NoInfo) } } } impl Drop for XkbInfo { fn drop(&mut self) { unsafe { c::g_object_unref(self.0) } } } #[derive(Clone, Debug, PartialEq)] pub struct Translation<'a>(pub &'a str); impl<'a> Translation<'a> { pub fn to_owned(&'a self) -> OwnedTranslation { OwnedTranslation(self.0.to_owned()) } } #[derive(Clone, Debug, PartialEq)] pub struct OwnedTranslation(pub String); fn cstring_safe(s: &str) -> CString { CString::new(s) .unwrap_or(CString::new("").unwrap()) } pub fn compare_current_locale(a: &str, b: &str) -> cmp::Ordering { let a = cstring_safe(a); let b = cstring_safe(b); let a = a.as_ptr(); let b = b.as_ptr(); let result = unsafe { c::strcoll(a, b) }; if result == 0 { cmp::Ordering::Equal } else if result > 0 { cmp::Ordering::Greater } else if result < 0 { cmp::Ordering::Less } else { unreachable!() } }