use std::char; use std::convert::TryFrom; use std::iter::once; use anyhow::{bail, Result}; pub trait Wide: Copy { fn encode_char(c: char) -> Result; fn encode_str(text: &str) -> Vec; fn encode_str_c(text: &str) -> Vec; fn decode_char(c: Self) -> Result; fn decode_str(iter: I) -> Result where I: IntoIterator; fn decode_str_c(iter: I) -> Result where I: IntoIterator, ::IntoIter: DoubleEndedIterator; } impl Wide for u16 { fn encode_char(c: char) -> Result { if c.len_utf16() == 1 { let mut v = [0]; c.encode_utf16(&mut v); Ok(v[0]) } else { bail!("{:?} does not fit within one UTF-16 character", c) } } fn encode_str(text: &str) -> Vec { text.encode_utf16().collect() } fn encode_str_c(text: &str) -> Vec { text.encode_utf16().chain(once(0)).collect() } fn decode_char(c: Self) -> Result { match char::decode_utf16(once(c)).next() { Some(r) => Ok(r?), None => unreachable!(), } } fn decode_str(iter: I) -> Result where I: IntoIterator, { let iter = char::decode_utf16(iter); let (lower_bound, _) = iter.size_hint(); let mut s = String::with_capacity(lower_bound); for r in iter { let c = r?; s.push(c); } Ok(s) } fn decode_str_c(iter: I) -> Result where I: IntoIterator, ::IntoIter: DoubleEndedIterator, { let mut iter = iter.into_iter(); // Make sure we end in a nul-terminator. match iter.next_back() { Some(0) => {} Some(v) => bail!("invalid terminator for C-style string: {:x}", v), None => bail!("missing terminator for C-style string"), } Self::decode_str(iter) } } impl Wide for u32 { fn encode_char(c: char) -> Result { Ok(c as u32) } fn encode_str(text: &str) -> Vec { text.chars().map(|c| c as u32).collect() } fn encode_str_c(text: &str) -> Vec { text.chars().map(|c| c as u32).chain(once(0)).collect() } fn decode_char(c: Self) -> Result { Ok(char::try_from(c)?) } fn decode_str(iter: I) -> Result where I: IntoIterator, { let iter = iter.into_iter(); let (lower_bound, _) = iter.size_hint(); let mut s = String::with_capacity(lower_bound); for c in iter { match char::from_u32(c) { Some(c) => s.push(c), None => bail!("invalid char: {:x}", c), } } Ok(s) } fn decode_str_c(iter: I) -> Result where I: IntoIterator, ::IntoIter: DoubleEndedIterator, { let mut iter = iter.into_iter(); // Make sure we end in a nul-terminator. match iter.next_back() { Some(0) => {} Some(v) => bail!("invalid terminator for C-style string: {}", v), None => bail!("missing terminator for C-style string"), } Self::decode_str(iter) } } impl Wide for i16 { fn encode_char(c: char) -> Result { u16::encode_char(c).map(|c| c as i16) } fn encode_str(text: &str) -> Vec { text.encode_utf16().map(|c| c as i16).collect() } fn encode_str_c(text: &str) -> Vec { text.encode_utf16() .map(|c| c as i16) .chain(once(0)) .collect() } fn decode_char(c: Self) -> Result { u16::decode_char(c as u16) } fn decode_str(iter: I) -> Result where I: IntoIterator, { u16::decode_str(iter.into_iter().map(|c| c as u16)) } fn decode_str_c(iter: I) -> Result where I: IntoIterator, ::IntoIter: DoubleEndedIterator, { u16::decode_str_c(iter.into_iter().map(|c| c as u16)) } } impl Wide for i32 { fn encode_char(c: char) -> Result { Ok(c as i32) } fn encode_str(text: &str) -> Vec { text.chars().map(|c| c as i32).collect() } fn encode_str_c(text: &str) -> Vec { text.chars().map(|c| c as i32).chain(once(0)).collect() } fn decode_char(c: Self) -> Result { Ok(char::try_from(c as u32)?) } fn decode_str(iter: I) -> Result where I: IntoIterator, { u32::decode_str(iter.into_iter().map(|c| c as u32)) } fn decode_str_c(iter: I) -> Result where I: IntoIterator, ::IntoIter: DoubleEndedIterator, { u32::decode_str_c(iter.into_iter().map(|c| c as u32)) } }