wchar-impl 0.11.0

Internal implementation of wchar.
Documentation
use std::any::type_name;
use std::iter::once;

use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{Error, LitChar, Result};

use crate::parse::WCharType;

pub fn expand_char(ty: WCharType, c: LitChar) -> Result<TokenStream> {
    fn quote_char<T: Encode>(c: LitChar) -> Result<TokenStream> {
        match T::encode_char(c.value()) {
            Some(c) => Ok(quote::quote! { #c }),
            None => Err(Error::new(
                c.span(),
                format_args!(
                    "character does not fit within a {} wide character",
                    type_name::<T>()
                ),
            )),
        }
    }

    match ty {
        WCharType::U16(_) => quote_char::<u16>(c),
        WCharType::U32(_) => quote_char::<u32>(c),
        WCharType::I16(_) => quote_char::<i16>(c),
        WCharType::I32(_) => quote_char::<i32>(c),
    }
}

pub fn expand_str(ty: WCharType, text: &str) -> TokenStream {
    fn quote_str<T: Encode>(text: &str) -> TokenStream {
        let chars = T::encode_str(text);
        quote::quote! { &[#(#chars),*] }
    }

    match ty {
        WCharType::U16(_) => quote_str::<u16>(text),
        WCharType::U32(_) => quote_str::<u32>(text),
        WCharType::I16(_) => quote_str::<i16>(text),
        WCharType::I32(_) => quote_str::<i32>(text),
    }
}

pub fn expand_str_c(ty: WCharType, text: &str) -> TokenStream {
    fn quote_str_c<T: Encode>(text: &str) -> TokenStream {
        let chars = T::encode_str_c(text);
        quote::quote! { &[#(#chars),*] }
    }

    match ty {
        WCharType::U16(_) => quote_str_c::<u16>(text),
        WCharType::U32(_) => quote_str_c::<u32>(text),
        WCharType::I16(_) => quote_str_c::<i16>(text),
        WCharType::I32(_) => quote_str_c::<i32>(text),
    }
}

pub trait Encode: Copy + ToTokens {
    fn wchar_type() -> WCharType;

    fn encode_char(c: char) -> Option<Self>;

    fn encode_str(s: &str) -> Vec<Self>;

    fn encode_str_c(s: &str) -> Vec<Self>;
}

impl Encode for u16 {
    fn wchar_type() -> WCharType {
        syn::parse_quote!(u16)
    }

    fn encode_char(c: char) -> Option<Self> {
        if c.len_utf16() == 1 {
            let mut buf = [0; 1];
            c.encode_utf16(&mut buf);
            Some(buf[0])
        } else {
            None
        }
    }

    fn encode_str(s: &str) -> Vec<Self> {
        s.encode_utf16().collect()
    }

    fn encode_str_c(s: &str) -> Vec<Self> {
        s.encode_utf16().chain(once(0)).collect()
    }
}

impl Encode for u32 {
    fn wchar_type() -> WCharType {
        syn::parse_quote!(u32)
    }

    fn encode_char(c: char) -> Option<Self> {
        Some(c as u32)
    }

    fn encode_str(s: &str) -> Vec<Self> {
        s.chars().map(|c| c as u32).collect()
    }

    fn encode_str_c(s: &str) -> Vec<Self> {
        s.chars().map(|c| c as u32).chain(once(0)).collect()
    }
}

impl Encode for i16 {
    fn wchar_type() -> WCharType {
        syn::parse_quote!(i16)
    }

    fn encode_char(c: char) -> Option<Self> {
        u16::encode_char(c).map(|c| c as i16)
    }

    fn encode_str(s: &str) -> Vec<Self> {
        s.encode_utf16().map(|c| c as i16).collect()
    }

    fn encode_str_c(s: &str) -> Vec<Self> {
        s.encode_utf16().map(|c| c as i16).chain(once(0)).collect()
    }
}

impl Encode for i32 {
    fn wchar_type() -> WCharType {
        syn::parse_quote!(i32)
    }

    fn encode_char(c: char) -> Option<Self> {
        Some(c as i32)
    }

    fn encode_str(s: &str) -> Vec<Self> {
        s.chars().map(|c| c as i32).collect()
    }

    fn encode_str_c(s: &str) -> Vec<Self> {
        s.chars().map(|c| c as i32).chain(once(0)).collect()
    }
}