125 lines
3.1 KiB
Rust
125 lines
3.1 KiB
Rust
/*! 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<String, Error> {
|
|
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!()
|
|
}
|
|
}
|