1use crate::ffi::wchar_t;
2use crate::{ffi, Error, LCMSResult, Locale};
3use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
4use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
5use std::ffi::CString;
6use std::fmt;
7use std::mem;
8use std::ptr;
9
10foreign_type! {
11 pub unsafe type MLU {
14 type CType = ffi::MLU;
15 fn drop = ffi::cmsMLUfree;
16 }
17}
18
19impl MLU {
20 #[track_caller]
22 #[inline]
23 #[must_use]
24 pub fn new(items: usize) -> Self {
25 unsafe {
26 let handle = ffi::cmsMLUalloc(ptr::null_mut(), items as u32);
27 assert!(!handle.is_null());
28 MLU::from_ptr(handle)
29 }
30 }
31}
32
33impl MLURef {
34 pub fn set_text_ascii(&mut self, text: &str, locale: Locale) -> bool {
36 let Ok(cstr) = CString::new(text) else { return false };
37 unsafe {
38 ffi::cmsMLUsetASCII(
39 (self as *mut Self).cast(),
40 locale.language_ptr(),
41 locale.country_ptr(),
42 cstr.as_ptr(),
43 ) != 0
44 }
45 }
46
47 pub fn set_text(&mut self, text: &str, locale: Locale) -> bool {
49 let chars: Vec<_> = text
50 .chars()
51 .map(|c| c as wchar_t)
52 .chain([0 as wchar_t])
53 .collect();
54
55 unsafe {
56 ffi::cmsMLUsetWide(
57 (self as *mut Self).cast(),
58 locale.language_ptr(),
59 locale.country_ptr(),
60 chars[..].as_ptr(),
61 ) != 0
62 }
63 }
64
65 pub fn text_ascii(&self, locale: Locale) -> LCMSResult<String> {
67 let len = unsafe {
68 ffi::cmsMLUgetASCII(
69 self.as_ptr(),
70 locale.language_ptr(),
71 locale.country_ptr(),
72 ptr::null_mut(),
73 0,
74 )
75 };
76 if len == 0 {
77 return Err(Error::MissingData);
78 }
79 let mut buf = vec![0u8; len as usize];
80 unsafe {
81 ffi::cmsMLUgetASCII(
82 self.as_ptr(),
83 locale.language_ptr(),
84 locale.country_ptr(),
85 buf[..].as_mut_ptr().cast(),
86 len,
87 );
88 if let Some(0) = buf.pop() { String::from_utf8(buf).map_err(|_| Error::InvalidString)
90 } else {
91 Err(Error::InvalidString)
92 }
93 }
94 }
95
96 pub fn text(&self, locale: Locale) -> LCMSResult<String> {
98 let len_bytes = unsafe {
99 ffi::cmsMLUgetWide(
100 self.as_ptr(),
101 locale.language_ptr(),
102 locale.country_ptr(),
103 ptr::null_mut(),
104 0,
105 )
106 };
107 let len_wchars = len_bytes as usize / mem::size_of::<wchar_t>();
108 if len_wchars == 0 || (len_bytes & 1) != 0 {
109 return Err(Error::MissingData);
110 }
111 let mut buf = vec![0 as wchar_t; len_wchars];
112 unsafe {
113 ffi::cmsMLUgetWide(
114 self.as_ptr(),
115 locale.language_ptr(),
116 locale.country_ptr(),
117 buf[..].as_mut_ptr().cast::<wchar_t>(),
118 len_bytes,
119 );
120 if let Some(0) = buf.pop() { Ok(decode_utf16(buf.into_iter().map(|c| c as u16))
122 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
123 .collect())
124 } else {
125 Err(Error::InvalidString)
126 }
127 }
128 }
129
130 #[must_use]
132 pub fn tanslations(&self) -> Vec<Locale> {
133 let count = unsafe { ffi::cmsMLUtranslationsCount(self.as_ptr()) };
134 let mut out = vec![Locale::none(); count as usize];
135
136 let mut i = 0;
137 out.retain_mut(|locale| {
138 let ok = unsafe {
139 ffi::cmsMLUtranslationsCodes(
140 self.as_ptr(),
141 i,
142 locale.language_ptr_mut(),
143 locale.country_ptr_mut(),
144 ) != 0
145 };
146 i += 1;
147 ok
148 });
149 out
150 }
151
152 pub fn tanslation(&self, locale: Locale) -> LCMSResult<Locale> {
154 let mut out = Locale::none();
155 if unsafe {
156 ffi::cmsMLUgetTranslation(
157 self.as_ptr(),
158 locale.language_ptr(),
159 locale.country_ptr(),
160 out.language_ptr_mut(),
161 out.country_ptr_mut(),
162 ) != 0
163 } {
164 Ok(out)
165 } else {
166 Err(Error::MissingData)
167 }
168 }
169}
170
171impl fmt::Debug for MLURef {
172 #[cold]
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 let t = self.text(Locale::none());
175 write!(
176 f,
177 "MLU({:?} {:?})",
178 if let Ok(ref t) = t { t } else { "None" },
179 self.tanslations()
180 )
181 }
182}
183
184#[test]
185fn mlu() {
186 let _ = MLU::new(0);
187 let mut m = MLU::new(1);
188 assert!(m.set_text("Hello 世界!", Locale::none()));
189 assert_eq!(Ok("Hello 世界!".to_owned()), m.text(Locale::none()));
190 assert!(!m.set_text_ascii("エッロル", Locale::none()));
191
192 assert!(m.set_text("a", Locale::new("en_US")));
193 assert_eq!("a", m.text_ascii(Locale::new("en_US")).unwrap());
194
195 let mut m = MLU::new(1);
196 assert!(m.set_text_ascii("OK", Locale::none()));
197 assert_eq!("OK", m.text_ascii(Locale::none()).unwrap());
198}