gettextrs/getters.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
//! Query gettext configuration.
//!
//! There are just a few settings in gettext. The only required one is the message domain, set
//! using [`textdomain`][::textdomain]; the other two are the path where translations are searched
//! for, and the encoding to which the messages should be converted.
//!
//! The underlying C API uses the same functions both as setters and as getters: to get the current
//! value, you just pass `NULL` as an argument. This is ergonomic in C, but not in Rust: wrapping
//! everything in `Option`s is a tad ugly. That's why this crate provides getters as separate
//! functions. They're in a module of their own to prevent them from clashing with any functions
//! that the underlying C API might gain in the future.
extern crate gettext_sys as ffi;
use std::ffi::{CStr, CString};
use std::io;
use std::path::PathBuf;
use std::ptr;
/// Get currently set message domain.
///
/// If you want to *set* the domain, rather than getting its current value, use
/// [`textdomain`][::textdomain].
///
/// For more information, see [textdomain(3)][].
///
/// [textdomain(3)]: https://www.man7.org/linux/man-pages/man3/textdomain.3.html
pub fn current_textdomain() -> Result<Vec<u8>, io::Error> {
unsafe {
let result = ffi::textdomain(ptr::null());
if result.is_null() {
Err(io::Error::last_os_error())
} else {
Ok(CStr::from_ptr(result).to_bytes().to_owned())
}
}
}
/// Get base directory for the given domain.
///
/// If you want to *set* the directory, rather than querying its current value, use
/// [`bindtextdomain`][::bindtextdomain].
///
/// For more information, see [bindtextdomain(3)][].
///
/// [bindtextdomain(3)]: https://www.man7.org/linux/man-pages/man3/bindtextdomain.3.html
///
/// # Panics
///
/// Panics if `domainname` contains an internal 0 byte, as such values can't be passed to the
/// underlying C API.
pub fn domain_directory<T: Into<Vec<u8>>>(domainname: T) -> Result<PathBuf, io::Error> {
let domainname = CString::new(domainname).expect("`domainname` contains an internal 0 byte");
#[cfg(windows)]
{
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
unsafe {
let mut ptr = ffi::wbindtextdomain(domainname.as_ptr(), ptr::null());
if ptr.is_null() {
Err(io::Error::last_os_error())
} else {
let mut result = vec![];
while *ptr != 0_u16 {
result.push(*ptr);
ptr = ptr.offset(1);
}
Ok(PathBuf::from(OsString::from_wide(&result)))
}
}
}
#[cfg(not(windows))]
{
use std::ffi::OsString;
use std::os::unix::ffi::OsStringExt;
unsafe {
let result = ffi::bindtextdomain(domainname.as_ptr(), ptr::null());
if result.is_null() {
Err(io::Error::last_os_error())
} else {
let result = CStr::from_ptr(result);
Ok(PathBuf::from(OsString::from_vec(
result.to_bytes().to_vec(),
)))
}
}
}
}
/// Get encoding of translated messages for given domain.
///
/// Returns `None` if encoding is not set.
///
/// If you want to *set* an encoding, rather than get the current one, use
/// [`bind_textdomain_codeset`][::bind_textdomain_codeset].
///
/// For more information, see [bind_textdomain_codeset(3)][].
///
/// [bind_textdomain_codeset(3)]: https://www.man7.org/linux/man-pages/man3/bind_textdomain_codeset.3.html
///
/// # Panics
///
/// Panics if:
/// * `domainname` contains an internal 0 byte, as such values can't be passed to the underlying
/// C API;
/// * the result is not in UTF-8 (which shouldn't happen as the results should always be ASCII, as
/// they're just codeset names).
pub fn textdomain_codeset<T: Into<Vec<u8>>>(domainname: T) -> Result<Option<String>, io::Error> {
let domainname = CString::new(domainname).expect("`domainname` contains an internal 0 byte");
unsafe {
let result = ffi::bind_textdomain_codeset(domainname.as_ptr(), ptr::null());
if result.is_null() {
let error = io::Error::last_os_error();
if let Some(0) = error.raw_os_error() {
return Ok(None);
} else {
return Err(error);
}
} else {
let result = CStr::from_ptr(result)
.to_str()
.expect("`bind_textdomain_codeset()` returned non-UTF-8 string")
.to_owned();
Ok(Some(result))
}
}
}