#![warn(
missing_docs,
missing_debug_implementations,
future_incompatible,
nonstandard_style,
rust_2018_compatibility,
rust_2018_idioms,
rust_2021_compatibility,
unused,
clippy::all,
clippy::cargo
)]
#[macro_use]
pub mod constants;
use std::convert::TryFrom;
use std::fmt;
#[derive(Debug)]
pub enum LcidLookupError {
ReservedBits(u32, u32),
SortIdBits(u32, u32),
Reserved(u32, &'static str),
ReservedUnknown(u32),
NeitherDefinedNorReserved(u32),
Undefined(u32),
Temporary(u32),
}
impl fmt::Display for LcidLookupError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ReservedBits(lcid, bits) => write!(
f,
"LCID {0}/{0:#06x} has reserved bits set: {1}/{1:#x}",
lcid, bits
),
Self::SortIdBits(lcid, bits) => write!(
f,
"LCID {0}/{0:#06x} has sort ID bits set: {1}/{1:#x}",
lcid, bits
),
Self::Reserved(lcid, language) => {
write!(f, "LCID {0}/{0:#06x} is reserved (`{1}`)", lcid, language)
}
Self::ReservedUnknown(lcid) => {
write!(f, "LCID {0}/{0:#06x} is reserved (<unknown>)", lcid)
}
Self::NeitherDefinedNorReserved(lcid) => {
write!(f, "LCID {0}/{0:#06x} is neither defined nor reserved", lcid)
}
Self::Undefined(lcid) => write!(f, "LCID {0}/{0:#06x} is undefined", lcid),
Self::Temporary(lcid) => write!(f, "LCID {0}/{0:#06x} is temporary", lcid),
}
}
}
impl std::error::Error for LcidLookupError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
#[derive(Debug)]
pub enum NameLookupError {
Reserved(&'static str, u32),
Undefined(String),
}
impl fmt::Display for NameLookupError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Reserved(name, lcid) => {
write!(f, "Name `{0}` is reserved ({1}/{1:#06x})", name, lcid)
}
Self::Undefined(name) => write!(f, "Name `{0}` is undefined", name),
}
}
}
impl std::error::Error for NameLookupError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum AnsiCodePage {
Windows874 = 874,
ShiftJIS = 932,
GB2312 = 936,
KsC5601 = 949,
Big5 = 950,
Windows1250 = 1250,
Windows1251 = 1251,
Windows1252 = 1252,
Windows1253 = 1253,
Windows1254 = 1254,
Windows1255 = 1255,
Windows1256 = 1256,
Windows1257 = 1257,
Windows1258 = 1258,
}
impl From<AnsiCodePage> for u32 {
fn from(value: AnsiCodePage) -> u32 {
value as u32
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LanguageId {
pub name: &'static str,
pub lcid: u32,
pub english_name: &'static str,
pub iso639_two_letter: &'static str,
pub iso639_three_letter: &'static str,
pub windows_three_letter: &'static str,
pub ansi_code_page: Option<AnsiCodePage>,
}
const PRIMARY_LANG_ID_MASK: u32 = 0x3ff;
const SUB_LANG_ID_MASK: u32 = 0x3f;
const RESERVED_BITS_MASK: u32 = 0xfff;
const SORT_ID_BITS_MASK: u32 = 0xf;
const PRIMARY_LANG_ID_SHIFT: u32 = 0;
const SUB_LANG_ID_SHIFT: u32 = 10;
const RESERVED_BITS_SHIFT: u32 = 20;
const SORT_ID_BITS_SHIFT: u32 = 16;
impl LanguageId {
#[inline]
pub fn primary_language_id(&self) -> u32 {
(self.lcid >> PRIMARY_LANG_ID_SHIFT) & PRIMARY_LANG_ID_MASK
}
#[inline]
pub fn sub_language_id(&self) -> u32 {
(self.lcid >> SUB_LANG_ID_SHIFT) & SUB_LANG_ID_MASK
}
}
impl TryFrom<u32> for &'static LanguageId {
type Error = LcidLookupError;
fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
let reserved_bits = (value >> RESERVED_BITS_SHIFT) & RESERVED_BITS_MASK;
if reserved_bits != 0 {
return Err(Self::Error::ReservedBits(value, reserved_bits));
}
let sort_id_bits = (value >> SORT_ID_BITS_SHIFT) & SORT_ID_BITS_MASK;
if sort_id_bits != 0 {
return Err(Self::Error::SortIdBits(value, sort_id_bits));
}
parse_lcid!(value)
}
}
impl TryFrom<&str> for &'static LanguageId {
type Error = NameLookupError;
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
parse_name!(value)
}
}
#[cfg(test)]
mod tests;