Skip to main content

tds_driver/tds/
collation.rs

1//! legacy implementation of collations (or codepages rather) for dealing with varchar's with legacy databases
2//! references [1] which has some mappings from the katmai (SQL Server 2008) source code and is a TDS driver
3//! directly from microsoft
4//! [2] is helpful to map CP1234 to the appropriate encoding
5//!
6//! [1] <https://github.com/Microsoft/mssql-jdbc/blob/eb14f63077c47ef1fc1c690deb8cfab602baeb85/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java>
7//! [2] <https://github.com/lifthrasiir/rust-encoding/blob/496823171f15d9b9446b2ec3fb7765f22346256b/src/label.rs#L282>
8
9use encoding_rs::Encoding;
10use std::fmt;
11
12use crate::error::Error;
13
14/// The collation of a character column, describing its locale (LCID), sort
15/// order and code page.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct Collation {
18    /// LCID ColFlags Version
19    info: u32,
20    /// Sortid
21    sort_id: u8,
22}
23
24impl Collation {
25    /// Create a new collation from a raw LCID/flags/version word and sort id.
26    pub fn new(info: u32, sort_id: u8) -> Self {
27        Self { info, sort_id }
28    }
29
30    /// return the locale id part of the LCID (the specification here uses ambiguous terms)
31    pub fn lcid(&self) -> u16 {
32        (self.info & 0xffff) as u16
33    }
34
35    /// The sort id of the collation.
36    pub fn sort_id(&self) -> u8 {
37        self.sort_id
38    }
39
40    /// The raw LCID/flags/version word of the collation.
41    pub fn info(&self) -> u32 {
42        self.info
43    }
44
45    /// return an encoding for a given collation
46    ///
47    /// Fails for the CP437/CP850 SQL collations (sort IDs 30-61), which
48    /// `encoding_rs` lacks; the driver itself still reads and writes them.
49    pub fn encoding(&self) -> crate::Result<&'static Encoding> {
50        // Checked first, as SqlClient does: a UTF-8 collation keeps the LCID of
51        // its locale, which would otherwise select a single-byte code page.
52        if self.info & UTF8_FLAG != 0 {
53            return Ok(encoding_rs::UTF_8);
54        }
55
56        let res = if self.sort_id == 0 {
57            lcid_to_encoding(self.lcid())
58        } else {
59            sortid_to_encoding(self.sort_id)
60        };
61
62        res.ok_or_else(|| {
63            Error::Encoding(
64                format!(
65                    "encoding: unspported encoding (LCID: {:#04x}, sort ID: {})",
66                    self.lcid(),
67                    self.sort_id(),
68                )
69                .into(),
70            )
71        })
72    }
73
74    /// The character set used for this collation's `char`/`varchar`/`text`.
75    pub(crate) fn charset(&self) -> crate::Result<Charset> {
76        match self.sort_id {
77            30..=35 => Ok(Charset::Oem(&CP437)),
78            40..=45 | 49 | 55..=61 => Ok(Charset::Oem(&CP850)),
79            _ => self.encoding().map(Charset::Encoding),
80        }
81    }
82}
83
84impl fmt::Display for Collation {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        match self.charset() {
87            Ok(Charset::Encoding(encoding)) => write!(f, "{}", encoding.name()),
88            Ok(Charset::Oem(table)) if std::ptr::eq(table, &CP437) => write!(f, "IBM437"),
89            Ok(Charset::Oem(_)) => write!(f, "IBM850"),
90            _ => write!(f, "None"),
91        }
92    }
93}
94
95/// The collation's fUTF8 bit; SqlClient's `TdsEnums.UTF8_IN_TDSCOLLATION`.
96const UTF8_FLAG: u32 = 0x0400_0000;
97
98#[derive(Clone, Copy, Debug)]
99pub(crate) enum Charset {
100    Encoding(&'static Encoding),
101    /// A single-byte code page that is ASCII below 0x80; the table holds the
102    /// characters for bytes 0x80..=0xFF.
103    Oem(&'static [char; 128]),
104}
105
106pub(crate) fn decode_oem(table: &[char; 128], buf: &[u8]) -> String {
107    buf.iter()
108        .map(|&b| match b {
109            0..=0x7f => b as char,
110            _ => table[b as usize - 0x80],
111        })
112        .collect()
113}
114
115pub(crate) fn encode_oem(table: &[char; 128], s: &str) -> crate::Result<Vec<u8>> {
116    s.chars()
117        .map(|c| match c {
118            '\0'..='\x7f' => Ok(c as u8),
119            _ => table
120                .iter()
121                .position(|&t| t == c)
122                .map(|i| 0x80 + i as u8)
123                .ok_or_else(|| {
124                    Error::Encoding(
125                        format!("{c:?} has no representation in the OEM code page").into(),
126                    )
127                }),
128        })
129        .collect()
130}
131
132// Bytes 0x80..=0xFF as SQL Server 2022 converts them to nvarchar.
133#[rustfmt::skip]
134static CP437: [char; 128] = [
135    'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è', 'ï', 'î', 'ì', 'Ä', 'Å',
136    'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥', '₧', 'ƒ',
137    'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '⌐', '¬', '½', '¼', '¡', '«', '»',
138    '░', '▒', '▓', '│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗', '╝', '╜', '╛', '┐',
139    '└', '┴', '┬', '├', '─', '┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧',
140    '╨', '╤', '╥', '╙', '╘', '╒', '╓', '╫', '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀',
141    'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩',
142    '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', '\u{a0}',
143];
144
145#[rustfmt::skip]
146static CP850: [char; 128] = [
147    'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è', 'ï', 'î', 'ì', 'Ä', 'Å',
148    'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', 'ø', '£', 'Ø', '×', 'ƒ',
149    'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '®', '¬', '½', '¼', '¡', '«', '»',
150    '░', '▒', '▓', '│', '┤', 'Á', 'Â', 'À', '©', '╣', '║', '╗', '╝', '¢', '¥', '┐',
151    '└', '┴', '┬', '├', '─', '┼', 'ã', 'Ã', '╚', '╔', '╩', '╦', '╠', '═', '╬', '¤',
152    'ð', 'Ð', 'Ê', 'Ë', 'È', 'ı', 'Í', 'Î', 'Ï', '┘', '┌', '█', '▄', '¦', 'Ì', '▀',
153    'Ó', 'ß', 'Ô', 'Ò', 'õ', 'Õ', 'µ', 'þ', 'Þ', 'Ú', 'Û', 'Ù', 'ý', 'Ý', '¯', '´',
154    '\u{ad}', '±', '‗', '¾', '¶', '§', '÷', '¸', '°', '¨', '·', '¹', '³', '²', '■', '\u{a0}',
155];
156
157/// https://github.com/Microsoft/mssql-jdbc/blob/eb14f63077c47ef1fc1c690deb8cfab602baeb85/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java#L102-L310
158/// maps an LCID (it's locale part which is only 2 bytes) to a codepage
159///
160/// generate the code below from source code:
161/// 1. (regex)replace: (.*?)\((.*?),(.*?)\) with $2 => $3
162/// 2. replace: Encoding.CP(.*?) with encoding::all::WINDOWS_$1
163/// 3. replace: Encoding.UNICODE with encoding::all::UTF16_LE
164///
165/// the unimplemented!() one's are not supported by rust-encoding
166pub fn lcid_to_encoding(locale: u16) -> Option<&'static Encoding> {
167    match locale {
168        0x0401 => Some(encoding_rs::WINDOWS_1256),
169        0x0402 => Some(encoding_rs::WINDOWS_1251),
170        0x0403 => Some(encoding_rs::WINDOWS_1252),
171        // CP950
172        0x0404 | 0x0c04 | 0x1404 => Some(encoding_rs::BIG5),
173        0x0405 => Some(encoding_rs::WINDOWS_1250),
174        0x0406 => Some(encoding_rs::WINDOWS_1252),
175        0x0407 => Some(encoding_rs::WINDOWS_1252),
176        0x0408 => Some(encoding_rs::WINDOWS_1253),
177        0x0409 => Some(encoding_rs::WINDOWS_1252),
178        0x040a => Some(encoding_rs::WINDOWS_1252),
179        0x040b => Some(encoding_rs::WINDOWS_1252),
180        0x040c => Some(encoding_rs::WINDOWS_1252),
181        0x040d => Some(encoding_rs::WINDOWS_1255),
182        0x040e => Some(encoding_rs::WINDOWS_1250),
183        0x040f => Some(encoding_rs::WINDOWS_1252),
184        0x0410 => Some(encoding_rs::WINDOWS_1252),
185        // CP932
186        0x0411 => Some(encoding_rs::SHIFT_JIS),
187        0x0412 => Some(encoding_rs::EUC_KR),
188        0x0413 => Some(encoding_rs::WINDOWS_1252),
189        0x0414 => Some(encoding_rs::WINDOWS_1252),
190        0x0415 => Some(encoding_rs::WINDOWS_1250),
191        0x0416 => Some(encoding_rs::WINDOWS_1252),
192        0x0417 => Some(encoding_rs::WINDOWS_1252),
193        0x0418 => Some(encoding_rs::WINDOWS_1250),
194        0x0419 => Some(encoding_rs::WINDOWS_1251),
195        0x041a => Some(encoding_rs::WINDOWS_1250),
196        0x041b => Some(encoding_rs::WINDOWS_1250),
197        0x041c => Some(encoding_rs::WINDOWS_1250),
198        0x041d => Some(encoding_rs::WINDOWS_1252),
199        0x041e => Some(encoding_rs::WINDOWS_874),
200        0x041f => Some(encoding_rs::WINDOWS_1254),
201        0x0420 => Some(encoding_rs::WINDOWS_1256),
202        0x0421 => Some(encoding_rs::WINDOWS_1252),
203        0x0422 => Some(encoding_rs::WINDOWS_1251),
204        0x0423 => Some(encoding_rs::WINDOWS_1251),
205        0x0424 => Some(encoding_rs::WINDOWS_1250),
206        0x0425 => Some(encoding_rs::WINDOWS_1257),
207        0x0426 => Some(encoding_rs::WINDOWS_1257),
208        0x0427 => Some(encoding_rs::WINDOWS_1257),
209        0x0428 => Some(encoding_rs::WINDOWS_1251),
210        0x0429 => Some(encoding_rs::WINDOWS_1256),
211        0x042a => Some(encoding_rs::WINDOWS_1258),
212        0x042b => Some(encoding_rs::WINDOWS_1252),
213        0x042c => Some(encoding_rs::WINDOWS_1254),
214        0x042d => Some(encoding_rs::WINDOWS_1252),
215        0x042e => Some(encoding_rs::WINDOWS_1252),
216        0x042f => Some(encoding_rs::WINDOWS_1251),
217        0x0432 => Some(encoding_rs::WINDOWS_1252),
218        0x0434 => Some(encoding_rs::WINDOWS_1252),
219        0x0435 => Some(encoding_rs::WINDOWS_1252),
220        0x0436 => Some(encoding_rs::WINDOWS_1252),
221        0x0437 => Some(encoding_rs::WINDOWS_1252),
222        0x0438 => Some(encoding_rs::WINDOWS_1252),
223        0x0439 => Some(encoding_rs::UTF_16LE),
224        0x043a => Some(encoding_rs::UTF_16LE),
225        0x043b => Some(encoding_rs::WINDOWS_1252),
226        0x043e => Some(encoding_rs::WINDOWS_1252),
227        0x043f => Some(encoding_rs::WINDOWS_1251),
228        0x0440 => Some(encoding_rs::WINDOWS_1251),
229        0x0441 => Some(encoding_rs::WINDOWS_1252),
230        0x0442 => Some(encoding_rs::WINDOWS_1250),
231        0x0443 => Some(encoding_rs::WINDOWS_1254),
232        0x0444 => Some(encoding_rs::WINDOWS_1251),
233        0x0445 => Some(encoding_rs::UTF_16LE),
234        0x0446 => Some(encoding_rs::UTF_16LE),
235        0x0447 => Some(encoding_rs::UTF_16LE),
236        0x0448 => Some(encoding_rs::UTF_16LE),
237        0x0449 => Some(encoding_rs::UTF_16LE),
238        0x044a => Some(encoding_rs::UTF_16LE),
239        0x044b => Some(encoding_rs::UTF_16LE),
240        0x044c => Some(encoding_rs::UTF_16LE),
241        0x044d => Some(encoding_rs::UTF_16LE),
242        0x044e => Some(encoding_rs::UTF_16LE),
243        0x044f => Some(encoding_rs::UTF_16LE),
244        0x0450 => Some(encoding_rs::WINDOWS_1251),
245        0x0451 => Some(encoding_rs::UTF_16LE),
246        0x0452 => Some(encoding_rs::WINDOWS_1252),
247        0x0453 => Some(encoding_rs::UTF_16LE),
248        0x0454 => Some(encoding_rs::UTF_16LE),
249        0x0456 => Some(encoding_rs::WINDOWS_1252),
250        0x0457 => Some(encoding_rs::UTF_16LE),
251        0x045a => Some(encoding_rs::UTF_16LE),
252        0x045b => Some(encoding_rs::UTF_16LE),
253        0x045d => Some(encoding_rs::WINDOWS_1252),
254        0x045e => Some(encoding_rs::WINDOWS_1252),
255        0x0461 => Some(encoding_rs::UTF_16LE),
256        0x0462 => Some(encoding_rs::WINDOWS_1252),
257        0x0463 => Some(encoding_rs::UTF_16LE),
258        0x0464 => Some(encoding_rs::WINDOWS_1252),
259        0x0465 => Some(encoding_rs::UTF_16LE),
260        0x0468 => Some(encoding_rs::WINDOWS_1252),
261        0x046a => Some(encoding_rs::WINDOWS_1252),
262        0x046b => Some(encoding_rs::WINDOWS_1252),
263        0x046c => Some(encoding_rs::WINDOWS_1252),
264        0x046d => Some(encoding_rs::WINDOWS_1251),
265        0x046e => Some(encoding_rs::WINDOWS_1252),
266        0x046f => Some(encoding_rs::WINDOWS_1252),
267        0x0470 => Some(encoding_rs::WINDOWS_1252),
268        0x0478 => Some(encoding_rs::WINDOWS_1252),
269        0x047a => Some(encoding_rs::WINDOWS_1252),
270        0x047c => Some(encoding_rs::WINDOWS_1252),
271        0x047e => Some(encoding_rs::WINDOWS_1252),
272        0x0480 => Some(encoding_rs::WINDOWS_1256),
273        0x0481 => Some(encoding_rs::UTF_16LE),
274        0x0482 => Some(encoding_rs::WINDOWS_1252),
275        0x0483 => Some(encoding_rs::WINDOWS_1252),
276        0x0484 => Some(encoding_rs::WINDOWS_1252),
277        0x0485 => Some(encoding_rs::WINDOWS_1251),
278        0x0486 => Some(encoding_rs::WINDOWS_1252),
279        0x0487 => Some(encoding_rs::WINDOWS_1252),
280        0x0488 => Some(encoding_rs::WINDOWS_1252),
281        0x048c => Some(encoding_rs::WINDOWS_1256),
282        0x0801 => Some(encoding_rs::WINDOWS_1256),
283        // CP936
284        0x0804 | 0x1004 => Some(encoding_rs::GB18030),
285        0x0807 => Some(encoding_rs::WINDOWS_1252),
286        0x0809 => Some(encoding_rs::WINDOWS_1252),
287        0x080a => Some(encoding_rs::WINDOWS_1252),
288        0x080c => Some(encoding_rs::WINDOWS_1252),
289        0x0810 => Some(encoding_rs::WINDOWS_1252),
290        0x0813 => Some(encoding_rs::WINDOWS_1252),
291        0x0814 => Some(encoding_rs::WINDOWS_1252),
292        0x0816 => Some(encoding_rs::WINDOWS_1252),
293        0x081a => Some(encoding_rs::WINDOWS_1250),
294        0x081d => Some(encoding_rs::WINDOWS_1252),
295        0x0827 => Some(encoding_rs::WINDOWS_1257),
296        0x082c => Some(encoding_rs::WINDOWS_1251),
297        0x082e => Some(encoding_rs::WINDOWS_1252),
298        0x083b => Some(encoding_rs::WINDOWS_1252),
299        0x083c => Some(encoding_rs::WINDOWS_1252),
300        0x083e => Some(encoding_rs::WINDOWS_1252),
301        0x0843 => Some(encoding_rs::WINDOWS_1251),
302        0x0845 => Some(encoding_rs::UTF_16LE),
303        0x0850 => Some(encoding_rs::WINDOWS_1251),
304        0x085d => Some(encoding_rs::WINDOWS_1252),
305        0x085f => Some(encoding_rs::WINDOWS_1252),
306        0x086b => Some(encoding_rs::WINDOWS_1252),
307        0x0c01 => Some(encoding_rs::WINDOWS_1256),
308        0x0c07 => Some(encoding_rs::WINDOWS_1252),
309        0x0c09 => Some(encoding_rs::WINDOWS_1252),
310        0x0c0a => Some(encoding_rs::WINDOWS_1252),
311        0x0c0c => Some(encoding_rs::WINDOWS_1252),
312        0x0c1a => Some(encoding_rs::WINDOWS_1251),
313        0x0c3b => Some(encoding_rs::WINDOWS_1252),
314        0x0c6b => Some(encoding_rs::WINDOWS_1252),
315        0x1001 => Some(encoding_rs::WINDOWS_1256),
316        0x1007 => Some(encoding_rs::WINDOWS_1252),
317        0x1009 => Some(encoding_rs::WINDOWS_1252),
318        0x100a => Some(encoding_rs::WINDOWS_1252),
319        0x100c => Some(encoding_rs::WINDOWS_1252),
320        0x101a => Some(encoding_rs::WINDOWS_1250),
321        0x103b => Some(encoding_rs::WINDOWS_1252),
322        0x1401 => Some(encoding_rs::WINDOWS_1256),
323        0x1407 => Some(encoding_rs::WINDOWS_1252),
324        0x1409 => Some(encoding_rs::WINDOWS_1252),
325        0x140a => Some(encoding_rs::WINDOWS_1252),
326        0x140c => Some(encoding_rs::WINDOWS_1252),
327        0x141a => Some(encoding_rs::WINDOWS_1250),
328        0x143b => Some(encoding_rs::WINDOWS_1252),
329        0x1801 => Some(encoding_rs::WINDOWS_1256),
330        0x1809 => Some(encoding_rs::WINDOWS_1252),
331        0x180a => Some(encoding_rs::WINDOWS_1252),
332        0x180c => Some(encoding_rs::WINDOWS_1252),
333        0x181a => Some(encoding_rs::WINDOWS_1250),
334        0x183b => Some(encoding_rs::WINDOWS_1252),
335        0x1c01 => Some(encoding_rs::WINDOWS_1256),
336        0x1c09 => Some(encoding_rs::WINDOWS_1252),
337        0x1c0a => Some(encoding_rs::WINDOWS_1252),
338        0x1c1a => Some(encoding_rs::WINDOWS_1251),
339        0x1c3b => Some(encoding_rs::WINDOWS_1252),
340        0x2001 => Some(encoding_rs::WINDOWS_1256),
341        0x2009 => Some(encoding_rs::WINDOWS_1252),
342        0x200a => Some(encoding_rs::WINDOWS_1252),
343        0x201a => Some(encoding_rs::WINDOWS_1251),
344        0x203b => Some(encoding_rs::WINDOWS_1252),
345        0x2401 => Some(encoding_rs::WINDOWS_1256),
346        0x2409 => Some(encoding_rs::WINDOWS_1252),
347        0x240a => Some(encoding_rs::WINDOWS_1252),
348        0x243b => Some(encoding_rs::WINDOWS_1252),
349        0x2801 => Some(encoding_rs::WINDOWS_1256),
350        0x2809 => Some(encoding_rs::WINDOWS_1252),
351        0x280a => Some(encoding_rs::WINDOWS_1252),
352        0x2c01 => Some(encoding_rs::WINDOWS_1256),
353        0x2c09 => Some(encoding_rs::WINDOWS_1252),
354        0x2c0a => Some(encoding_rs::WINDOWS_1252),
355        0x3001 => Some(encoding_rs::WINDOWS_1256),
356        0x3009 => Some(encoding_rs::WINDOWS_1252),
357        0x300a => Some(encoding_rs::WINDOWS_1252),
358        0x3401 => Some(encoding_rs::WINDOWS_1256),
359        0x3409 => Some(encoding_rs::WINDOWS_1252),
360        0x340a => Some(encoding_rs::WINDOWS_1252),
361        0x3801 => Some(encoding_rs::WINDOWS_1256),
362        0x380a => Some(encoding_rs::WINDOWS_1252),
363        0x3c01 => Some(encoding_rs::WINDOWS_1256),
364        0x3c0a => Some(encoding_rs::WINDOWS_1252),
365        0x4001 => Some(encoding_rs::WINDOWS_1256),
366        0x4009 => Some(encoding_rs::WINDOWS_1252),
367        0x400a => Some(encoding_rs::WINDOWS_1252),
368        0x4409 => Some(encoding_rs::WINDOWS_1252),
369        0x440a => Some(encoding_rs::WINDOWS_1252),
370        0x4809 => Some(encoding_rs::WINDOWS_1252),
371        0x480a => Some(encoding_rs::WINDOWS_1252),
372        0x4c0a => Some(encoding_rs::WINDOWS_1252),
373        0x500a => Some(encoding_rs::WINDOWS_1252),
374        0x540a => Some(encoding_rs::WINDOWS_1252),
375        _ => None,
376    }
377}
378
379/// [1] https://github.com/Microsoft/mssql-jdbc/blob/eb14f63077c47ef1fc1c690deb8cfab602baeb85/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java#L362-L482
380/// [2] https://msdn.microsoft.com/de-de/library/ms144250(v=sql.105).aspx
381///
382/// [2] does only contain 3/4 of the content [1] contains, so the source code is again the better source of information
383///
384/// generate the code below from source code:
385/// 1. (regex)replace .*\((.*?),.*?,(.*?)\) with $1 => $2
386/// 2. see above/as above
387pub fn sortid_to_encoding(sort_id: u8) -> Option<&'static Encoding> {
388    match sort_id {
389        // 30 | 31 | 32 | 33 | 34 | 35 => Some(encoding_rs::WINDOWS_437),
390        // 40 | 41 | 42 | 43 | 44 | 45 | 49 => Some(encoding_rs::WINDOWS_850),
391        50 => Some(encoding_rs::WINDOWS_1252),
392        51 => Some(encoding_rs::WINDOWS_1252),
393        52 => Some(encoding_rs::WINDOWS_1252),
394        53 => Some(encoding_rs::WINDOWS_1252),
395        54 => Some(encoding_rs::WINDOWS_1252),
396        // 55 | 56 | 57 | 58 | 59 | 60 | 61 => Some(encoding_rs::WINDOWS_850),
397        71 => Some(encoding_rs::WINDOWS_1252),
398        72 => Some(encoding_rs::WINDOWS_1252),
399        73 => Some(encoding_rs::WINDOWS_1252),
400        74 => Some(encoding_rs::WINDOWS_1252),
401        75 => Some(encoding_rs::WINDOWS_1252),
402        80 => Some(encoding_rs::WINDOWS_1250),
403        81 => Some(encoding_rs::WINDOWS_1250),
404        82 => Some(encoding_rs::WINDOWS_1250),
405        83 => Some(encoding_rs::WINDOWS_1250),
406        84 => Some(encoding_rs::WINDOWS_1250),
407        85 => Some(encoding_rs::WINDOWS_1250),
408        86 => Some(encoding_rs::WINDOWS_1250),
409        87 => Some(encoding_rs::WINDOWS_1250),
410        88 => Some(encoding_rs::WINDOWS_1250),
411        89 => Some(encoding_rs::WINDOWS_1250),
412        90 => Some(encoding_rs::WINDOWS_1250),
413        91 => Some(encoding_rs::WINDOWS_1250),
414        92 => Some(encoding_rs::WINDOWS_1250),
415        93 => Some(encoding_rs::WINDOWS_1250),
416        94 => Some(encoding_rs::WINDOWS_1250),
417        95 => Some(encoding_rs::WINDOWS_1250),
418        96 => Some(encoding_rs::WINDOWS_1250),
419        97 => Some(encoding_rs::WINDOWS_1250),
420        98 => Some(encoding_rs::WINDOWS_1250),
421        104 => Some(encoding_rs::WINDOWS_1251),
422        105 => Some(encoding_rs::WINDOWS_1251),
423        106 => Some(encoding_rs::WINDOWS_1251),
424        107 => Some(encoding_rs::WINDOWS_1251),
425        108 => Some(encoding_rs::WINDOWS_1251),
426        112 => Some(encoding_rs::WINDOWS_1253),
427        113 => Some(encoding_rs::WINDOWS_1253),
428        114 => Some(encoding_rs::WINDOWS_1253),
429        120 => Some(encoding_rs::WINDOWS_1253),
430        121 => Some(encoding_rs::WINDOWS_1253),
431        122 => Some(encoding_rs::WINDOWS_1253),
432        124 => Some(encoding_rs::WINDOWS_1253),
433        128 => Some(encoding_rs::WINDOWS_1254),
434        129 => Some(encoding_rs::WINDOWS_1254),
435        130 => Some(encoding_rs::WINDOWS_1254),
436        136 => Some(encoding_rs::WINDOWS_1255),
437        137 => Some(encoding_rs::WINDOWS_1255),
438        138 => Some(encoding_rs::WINDOWS_1255),
439        144 => Some(encoding_rs::WINDOWS_1256),
440        145 => Some(encoding_rs::WINDOWS_1256),
441        146 => Some(encoding_rs::WINDOWS_1256),
442        152 => Some(encoding_rs::WINDOWS_1257),
443        153 => Some(encoding_rs::WINDOWS_1257),
444        154 => Some(encoding_rs::WINDOWS_1257),
445        155 => Some(encoding_rs::WINDOWS_1257),
446        156 => Some(encoding_rs::WINDOWS_1257),
447        157 => Some(encoding_rs::WINDOWS_1257),
448        158 => Some(encoding_rs::WINDOWS_1257),
449        159 => Some(encoding_rs::WINDOWS_1257),
450        160 => Some(encoding_rs::WINDOWS_1257),
451        183 => Some(encoding_rs::WINDOWS_1252),
452        184 => Some(encoding_rs::WINDOWS_1252),
453        185 => Some(encoding_rs::WINDOWS_1252),
454        186 => Some(encoding_rs::WINDOWS_1252),
455        // CP 932
456        192 | 193 | 200 => Some(encoding_rs::SHIFT_JIS),
457        194 => Some(encoding_rs::EUC_KR),
458        195 => Some(encoding_rs::EUC_KR),
459        // CP950
460        196 | 197 | 202 => Some(encoding_rs::BIG5),
461        // CP936 (GB18030 is an extension of it with more chars), should be backwards-compatible)
462        198 | 199 | 203 => Some(encoding_rs::GB18030),
463        201 => Some(encoding_rs::BIG5),
464        204 => Some(encoding_rs::WINDOWS_874),
465        205 => Some(encoding_rs::WINDOWS_874),
466        206 => Some(encoding_rs::WINDOWS_874),
467        210 => Some(encoding_rs::WINDOWS_1252),
468        211 => Some(encoding_rs::WINDOWS_1252),
469        212 => Some(encoding_rs::WINDOWS_1252),
470        213 => Some(encoding_rs::WINDOWS_1252),
471        214 => Some(encoding_rs::WINDOWS_1252),
472        215 => Some(encoding_rs::WINDOWS_1252),
473        216 => Some(encoding_rs::WINDOWS_1252),
474        217 => Some(encoding_rs::WINDOWS_1252),
475        _ => None,
476    }
477}
478
479#[cfg(test)]
480mod tests {
481    use super::*;
482
483    #[test]
484    fn accessors_split_info_and_sort_id() {
485        // info holds LCID in the low 16 bits plus flags/version in the high bits.
486        let collation = Collation::new(0x0020_0409, 52);
487        assert_eq!(collation.info(), 0x0020_0409);
488        assert_eq!(collation.lcid(), 0x0409);
489        assert_eq!(collation.sort_id(), 52);
490    }
491
492    #[test]
493    fn encoding_from_lcid_when_sort_id_zero() {
494        // sort_id == 0 -> resolve via the LCID.
495        let collation = Collation::new(0x0409, 0);
496        let encoding = collation.encoding().expect("known LCID must resolve");
497        assert_eq!(encoding, encoding_rs::WINDOWS_1252);
498    }
499
500    #[test]
501    fn encoding_from_sort_id_when_present() {
502        // A non-zero sort_id takes precedence over the LCID.
503        let collation = Collation::new(0x0405, 80);
504        let encoding = collation.encoding().expect("known sort id must resolve");
505        assert_eq!(encoding, encoding_rs::WINDOWS_1250);
506    }
507
508    #[test]
509    fn encoding_unknown_lcid_errors() {
510        let collation = Collation::new(0xFFFF, 0);
511        let err = collation.encoding().unwrap_err();
512        assert!(matches!(err, Error::Encoding(_)));
513    }
514
515    #[test]
516    fn encoding_unknown_sort_id_errors() {
517        let collation = Collation::new(0x0409, 250);
518        assert!(collation.encoding().is_err());
519    }
520
521    #[test]
522    fn display_uses_encoding_name() {
523        let collation = Collation::new(0x0409, 0);
524        assert_eq!(format!("{}", collation), encoding_rs::WINDOWS_1252.name());
525    }
526
527    #[test]
528    fn display_falls_back_to_none_on_unknown() {
529        let collation = Collation::new(0xFFFF, 0);
530        assert_eq!(format!("{}", collation), "None");
531    }
532
533    #[test]
534    fn lcid_to_encoding_known_and_unknown() {
535        assert_eq!(lcid_to_encoding(0x0401), Some(encoding_rs::WINDOWS_1256));
536        assert_eq!(lcid_to_encoding(0x0404), Some(encoding_rs::BIG5));
537        assert_eq!(lcid_to_encoding(0x0411), Some(encoding_rs::SHIFT_JIS));
538        assert_eq!(lcid_to_encoding(0x0412), Some(encoding_rs::EUC_KR));
539        assert_eq!(lcid_to_encoding(0x0804), Some(encoding_rs::GB18030));
540        assert_eq!(lcid_to_encoding(0x0439), Some(encoding_rs::UTF_16LE));
541        assert_eq!(lcid_to_encoding(0x041e), Some(encoding_rs::WINDOWS_874));
542        assert_eq!(lcid_to_encoding(0x0000), None);
543    }
544
545    #[test]
546    fn sortid_to_encoding_known_and_unknown() {
547        assert_eq!(sortid_to_encoding(50), Some(encoding_rs::WINDOWS_1252));
548        assert_eq!(sortid_to_encoding(80), Some(encoding_rs::WINDOWS_1250));
549        assert_eq!(sortid_to_encoding(104), Some(encoding_rs::WINDOWS_1251));
550        assert_eq!(sortid_to_encoding(112), Some(encoding_rs::WINDOWS_1253));
551        assert_eq!(sortid_to_encoding(192), Some(encoding_rs::SHIFT_JIS));
552        assert_eq!(sortid_to_encoding(194), Some(encoding_rs::EUC_KR));
553        assert_eq!(sortid_to_encoding(196), Some(encoding_rs::BIG5));
554        assert_eq!(sortid_to_encoding(198), Some(encoding_rs::GB18030));
555        assert_eq!(sortid_to_encoding(204), Some(encoding_rs::WINDOWS_874));
556        assert_eq!(sortid_to_encoding(0), None);
557        assert_eq!(sortid_to_encoding(255), None);
558    }
559
560    #[test]
561    fn lcid_to_encoding_covers_all_documented_locales() {
562        let cases: &[(u16, &encoding_rs::Encoding)] = &[
563            (0x0401, encoding_rs::WINDOWS_1256),
564            (0x0402, encoding_rs::WINDOWS_1251),
565            (0x0403, encoding_rs::WINDOWS_1252),
566            (0x0404, encoding_rs::BIG5),
567            (0x0c04, encoding_rs::BIG5),
568            (0x1404, encoding_rs::BIG5),
569            (0x0405, encoding_rs::WINDOWS_1250),
570            (0x0406, encoding_rs::WINDOWS_1252),
571            (0x0407, encoding_rs::WINDOWS_1252),
572            (0x0408, encoding_rs::WINDOWS_1253),
573            (0x0409, encoding_rs::WINDOWS_1252),
574            (0x040a, encoding_rs::WINDOWS_1252),
575            (0x040b, encoding_rs::WINDOWS_1252),
576            (0x040c, encoding_rs::WINDOWS_1252),
577            (0x040d, encoding_rs::WINDOWS_1255),
578            (0x040e, encoding_rs::WINDOWS_1250),
579            (0x040f, encoding_rs::WINDOWS_1252),
580            (0x0410, encoding_rs::WINDOWS_1252),
581            (0x0411, encoding_rs::SHIFT_JIS),
582            (0x0412, encoding_rs::EUC_KR),
583            (0x0413, encoding_rs::WINDOWS_1252),
584            (0x0414, encoding_rs::WINDOWS_1252),
585            (0x0415, encoding_rs::WINDOWS_1250),
586            (0x0416, encoding_rs::WINDOWS_1252),
587            (0x0417, encoding_rs::WINDOWS_1252),
588            (0x0418, encoding_rs::WINDOWS_1250),
589            (0x0419, encoding_rs::WINDOWS_1251),
590            (0x041a, encoding_rs::WINDOWS_1250),
591            (0x041b, encoding_rs::WINDOWS_1250),
592            (0x041c, encoding_rs::WINDOWS_1250),
593            (0x041d, encoding_rs::WINDOWS_1252),
594            (0x041e, encoding_rs::WINDOWS_874),
595            (0x041f, encoding_rs::WINDOWS_1254),
596            (0x0420, encoding_rs::WINDOWS_1256),
597            (0x0421, encoding_rs::WINDOWS_1252),
598            (0x0422, encoding_rs::WINDOWS_1251),
599            (0x0423, encoding_rs::WINDOWS_1251),
600            (0x0424, encoding_rs::WINDOWS_1250),
601            (0x0425, encoding_rs::WINDOWS_1257),
602            (0x0426, encoding_rs::WINDOWS_1257),
603            (0x0427, encoding_rs::WINDOWS_1257),
604            (0x0428, encoding_rs::WINDOWS_1251),
605            (0x0429, encoding_rs::WINDOWS_1256),
606            (0x042a, encoding_rs::WINDOWS_1258),
607            (0x042b, encoding_rs::WINDOWS_1252),
608            (0x042c, encoding_rs::WINDOWS_1254),
609            (0x042d, encoding_rs::WINDOWS_1252),
610            (0x042e, encoding_rs::WINDOWS_1252),
611            (0x042f, encoding_rs::WINDOWS_1251),
612            (0x0432, encoding_rs::WINDOWS_1252),
613            (0x0434, encoding_rs::WINDOWS_1252),
614            (0x0435, encoding_rs::WINDOWS_1252),
615            (0x0436, encoding_rs::WINDOWS_1252),
616            (0x0437, encoding_rs::WINDOWS_1252),
617            (0x0438, encoding_rs::WINDOWS_1252),
618            (0x0439, encoding_rs::UTF_16LE),
619            (0x043a, encoding_rs::UTF_16LE),
620            (0x043b, encoding_rs::WINDOWS_1252),
621            (0x043e, encoding_rs::WINDOWS_1252),
622            (0x043f, encoding_rs::WINDOWS_1251),
623            (0x0440, encoding_rs::WINDOWS_1251),
624            (0x0441, encoding_rs::WINDOWS_1252),
625            (0x0442, encoding_rs::WINDOWS_1250),
626            (0x0443, encoding_rs::WINDOWS_1254),
627            (0x0444, encoding_rs::WINDOWS_1251),
628            (0x0445, encoding_rs::UTF_16LE),
629            (0x0446, encoding_rs::UTF_16LE),
630            (0x0447, encoding_rs::UTF_16LE),
631            (0x0448, encoding_rs::UTF_16LE),
632            (0x0449, encoding_rs::UTF_16LE),
633            (0x044a, encoding_rs::UTF_16LE),
634            (0x044b, encoding_rs::UTF_16LE),
635            (0x044c, encoding_rs::UTF_16LE),
636            (0x044d, encoding_rs::UTF_16LE),
637            (0x044e, encoding_rs::UTF_16LE),
638            (0x044f, encoding_rs::UTF_16LE),
639            (0x0450, encoding_rs::WINDOWS_1251),
640            (0x0451, encoding_rs::UTF_16LE),
641            (0x0452, encoding_rs::WINDOWS_1252),
642            (0x0453, encoding_rs::UTF_16LE),
643            (0x0454, encoding_rs::UTF_16LE),
644            (0x0456, encoding_rs::WINDOWS_1252),
645            (0x0457, encoding_rs::UTF_16LE),
646            (0x045a, encoding_rs::UTF_16LE),
647            (0x045b, encoding_rs::UTF_16LE),
648            (0x045d, encoding_rs::WINDOWS_1252),
649            (0x045e, encoding_rs::WINDOWS_1252),
650            (0x0461, encoding_rs::UTF_16LE),
651            (0x0462, encoding_rs::WINDOWS_1252),
652            (0x0463, encoding_rs::UTF_16LE),
653            (0x0464, encoding_rs::WINDOWS_1252),
654            (0x0465, encoding_rs::UTF_16LE),
655            (0x0468, encoding_rs::WINDOWS_1252),
656            (0x046a, encoding_rs::WINDOWS_1252),
657            (0x046b, encoding_rs::WINDOWS_1252),
658            (0x046c, encoding_rs::WINDOWS_1252),
659            (0x046d, encoding_rs::WINDOWS_1251),
660            (0x046e, encoding_rs::WINDOWS_1252),
661            (0x046f, encoding_rs::WINDOWS_1252),
662            (0x0470, encoding_rs::WINDOWS_1252),
663            (0x0478, encoding_rs::WINDOWS_1252),
664            (0x047a, encoding_rs::WINDOWS_1252),
665            (0x047c, encoding_rs::WINDOWS_1252),
666            (0x047e, encoding_rs::WINDOWS_1252),
667            (0x0480, encoding_rs::WINDOWS_1256),
668            (0x0481, encoding_rs::UTF_16LE),
669            (0x0482, encoding_rs::WINDOWS_1252),
670            (0x0483, encoding_rs::WINDOWS_1252),
671            (0x0484, encoding_rs::WINDOWS_1252),
672            (0x0485, encoding_rs::WINDOWS_1251),
673            (0x0486, encoding_rs::WINDOWS_1252),
674            (0x0487, encoding_rs::WINDOWS_1252),
675            (0x0488, encoding_rs::WINDOWS_1252),
676            (0x048c, encoding_rs::WINDOWS_1256),
677            (0x0801, encoding_rs::WINDOWS_1256),
678            (0x0804, encoding_rs::GB18030),
679            (0x1004, encoding_rs::GB18030),
680            (0x0807, encoding_rs::WINDOWS_1252),
681            (0x0809, encoding_rs::WINDOWS_1252),
682            (0x080a, encoding_rs::WINDOWS_1252),
683            (0x080c, encoding_rs::WINDOWS_1252),
684            (0x0810, encoding_rs::WINDOWS_1252),
685            (0x0813, encoding_rs::WINDOWS_1252),
686            (0x0814, encoding_rs::WINDOWS_1252),
687            (0x0816, encoding_rs::WINDOWS_1252),
688            (0x081a, encoding_rs::WINDOWS_1250),
689            (0x081d, encoding_rs::WINDOWS_1252),
690            (0x0827, encoding_rs::WINDOWS_1257),
691            (0x082c, encoding_rs::WINDOWS_1251),
692            (0x082e, encoding_rs::WINDOWS_1252),
693            (0x083b, encoding_rs::WINDOWS_1252),
694            (0x083c, encoding_rs::WINDOWS_1252),
695            (0x083e, encoding_rs::WINDOWS_1252),
696            (0x0843, encoding_rs::WINDOWS_1251),
697            (0x0845, encoding_rs::UTF_16LE),
698            (0x0850, encoding_rs::WINDOWS_1251),
699            (0x085d, encoding_rs::WINDOWS_1252),
700            (0x085f, encoding_rs::WINDOWS_1252),
701            (0x086b, encoding_rs::WINDOWS_1252),
702            (0x0c01, encoding_rs::WINDOWS_1256),
703            (0x0c07, encoding_rs::WINDOWS_1252),
704            (0x0c09, encoding_rs::WINDOWS_1252),
705            (0x0c0a, encoding_rs::WINDOWS_1252),
706            (0x0c0c, encoding_rs::WINDOWS_1252),
707            (0x0c1a, encoding_rs::WINDOWS_1251),
708            (0x0c3b, encoding_rs::WINDOWS_1252),
709            (0x0c6b, encoding_rs::WINDOWS_1252),
710            (0x1001, encoding_rs::WINDOWS_1256),
711            (0x1007, encoding_rs::WINDOWS_1252),
712            (0x1009, encoding_rs::WINDOWS_1252),
713            (0x100a, encoding_rs::WINDOWS_1252),
714            (0x100c, encoding_rs::WINDOWS_1252),
715            (0x101a, encoding_rs::WINDOWS_1250),
716            (0x103b, encoding_rs::WINDOWS_1252),
717            (0x1401, encoding_rs::WINDOWS_1256),
718            (0x1407, encoding_rs::WINDOWS_1252),
719            (0x1409, encoding_rs::WINDOWS_1252),
720            (0x140a, encoding_rs::WINDOWS_1252),
721            (0x140c, encoding_rs::WINDOWS_1252),
722            (0x141a, encoding_rs::WINDOWS_1250),
723            (0x143b, encoding_rs::WINDOWS_1252),
724            (0x1801, encoding_rs::WINDOWS_1256),
725            (0x1809, encoding_rs::WINDOWS_1252),
726            (0x180a, encoding_rs::WINDOWS_1252),
727            (0x180c, encoding_rs::WINDOWS_1252),
728            (0x181a, encoding_rs::WINDOWS_1250),
729            (0x183b, encoding_rs::WINDOWS_1252),
730            (0x1c01, encoding_rs::WINDOWS_1256),
731            (0x1c09, encoding_rs::WINDOWS_1252),
732            (0x1c0a, encoding_rs::WINDOWS_1252),
733            (0x1c1a, encoding_rs::WINDOWS_1251),
734            (0x1c3b, encoding_rs::WINDOWS_1252),
735            (0x2001, encoding_rs::WINDOWS_1256),
736            (0x2009, encoding_rs::WINDOWS_1252),
737            (0x200a, encoding_rs::WINDOWS_1252),
738            (0x201a, encoding_rs::WINDOWS_1251),
739            (0x203b, encoding_rs::WINDOWS_1252),
740            (0x2401, encoding_rs::WINDOWS_1256),
741            (0x2409, encoding_rs::WINDOWS_1252),
742            (0x240a, encoding_rs::WINDOWS_1252),
743            (0x243b, encoding_rs::WINDOWS_1252),
744            (0x2801, encoding_rs::WINDOWS_1256),
745            (0x2809, encoding_rs::WINDOWS_1252),
746            (0x280a, encoding_rs::WINDOWS_1252),
747            (0x2c01, encoding_rs::WINDOWS_1256),
748            (0x2c09, encoding_rs::WINDOWS_1252),
749            (0x2c0a, encoding_rs::WINDOWS_1252),
750            (0x3001, encoding_rs::WINDOWS_1256),
751            (0x3009, encoding_rs::WINDOWS_1252),
752            (0x300a, encoding_rs::WINDOWS_1252),
753            (0x3401, encoding_rs::WINDOWS_1256),
754            (0x3409, encoding_rs::WINDOWS_1252),
755            (0x340a, encoding_rs::WINDOWS_1252),
756            (0x3801, encoding_rs::WINDOWS_1256),
757            (0x380a, encoding_rs::WINDOWS_1252),
758            (0x3c01, encoding_rs::WINDOWS_1256),
759            (0x3c0a, encoding_rs::WINDOWS_1252),
760            (0x4001, encoding_rs::WINDOWS_1256),
761            (0x4009, encoding_rs::WINDOWS_1252),
762            (0x400a, encoding_rs::WINDOWS_1252),
763            (0x4409, encoding_rs::WINDOWS_1252),
764            (0x440a, encoding_rs::WINDOWS_1252),
765            (0x4809, encoding_rs::WINDOWS_1252),
766            (0x480a, encoding_rs::WINDOWS_1252),
767            (0x4c0a, encoding_rs::WINDOWS_1252),
768            (0x500a, encoding_rs::WINDOWS_1252),
769            (0x540a, encoding_rs::WINDOWS_1252),
770        ];
771
772        for (locale, expected) in cases {
773            assert_eq!(
774                lcid_to_encoding(*locale),
775                Some(*expected),
776                "locale {:#06x}",
777                locale
778            );
779        }
780    }
781
782    #[test]
783    fn sortid_to_encoding_covers_all_documented_sort_ids() {
784        let cases: &[(u8, &encoding_rs::Encoding)] = &[
785            (50, encoding_rs::WINDOWS_1252),
786            (51, encoding_rs::WINDOWS_1252),
787            (52, encoding_rs::WINDOWS_1252),
788            (53, encoding_rs::WINDOWS_1252),
789            (54, encoding_rs::WINDOWS_1252),
790            (71, encoding_rs::WINDOWS_1252),
791            (72, encoding_rs::WINDOWS_1252),
792            (73, encoding_rs::WINDOWS_1252),
793            (74, encoding_rs::WINDOWS_1252),
794            (75, encoding_rs::WINDOWS_1252),
795            (80, encoding_rs::WINDOWS_1250),
796            (81, encoding_rs::WINDOWS_1250),
797            (82, encoding_rs::WINDOWS_1250),
798            (83, encoding_rs::WINDOWS_1250),
799            (84, encoding_rs::WINDOWS_1250),
800            (85, encoding_rs::WINDOWS_1250),
801            (86, encoding_rs::WINDOWS_1250),
802            (87, encoding_rs::WINDOWS_1250),
803            (88, encoding_rs::WINDOWS_1250),
804            (89, encoding_rs::WINDOWS_1250),
805            (90, encoding_rs::WINDOWS_1250),
806            (91, encoding_rs::WINDOWS_1250),
807            (92, encoding_rs::WINDOWS_1250),
808            (93, encoding_rs::WINDOWS_1250),
809            (94, encoding_rs::WINDOWS_1250),
810            (95, encoding_rs::WINDOWS_1250),
811            (96, encoding_rs::WINDOWS_1250),
812            (97, encoding_rs::WINDOWS_1250),
813            (98, encoding_rs::WINDOWS_1250),
814            (104, encoding_rs::WINDOWS_1251),
815            (105, encoding_rs::WINDOWS_1251),
816            (106, encoding_rs::WINDOWS_1251),
817            (107, encoding_rs::WINDOWS_1251),
818            (108, encoding_rs::WINDOWS_1251),
819            (112, encoding_rs::WINDOWS_1253),
820            (113, encoding_rs::WINDOWS_1253),
821            (114, encoding_rs::WINDOWS_1253),
822            (120, encoding_rs::WINDOWS_1253),
823            (121, encoding_rs::WINDOWS_1253),
824            (122, encoding_rs::WINDOWS_1253),
825            (124, encoding_rs::WINDOWS_1253),
826            (128, encoding_rs::WINDOWS_1254),
827            (129, encoding_rs::WINDOWS_1254),
828            (130, encoding_rs::WINDOWS_1254),
829            (136, encoding_rs::WINDOWS_1255),
830            (137, encoding_rs::WINDOWS_1255),
831            (138, encoding_rs::WINDOWS_1255),
832            (144, encoding_rs::WINDOWS_1256),
833            (145, encoding_rs::WINDOWS_1256),
834            (146, encoding_rs::WINDOWS_1256),
835            (152, encoding_rs::WINDOWS_1257),
836            (153, encoding_rs::WINDOWS_1257),
837            (154, encoding_rs::WINDOWS_1257),
838            (155, encoding_rs::WINDOWS_1257),
839            (156, encoding_rs::WINDOWS_1257),
840            (157, encoding_rs::WINDOWS_1257),
841            (158, encoding_rs::WINDOWS_1257),
842            (159, encoding_rs::WINDOWS_1257),
843            (160, encoding_rs::WINDOWS_1257),
844            (183, encoding_rs::WINDOWS_1252),
845            (184, encoding_rs::WINDOWS_1252),
846            (185, encoding_rs::WINDOWS_1252),
847            (186, encoding_rs::WINDOWS_1252),
848            (192, encoding_rs::SHIFT_JIS),
849            (193, encoding_rs::SHIFT_JIS),
850            (200, encoding_rs::SHIFT_JIS),
851            (194, encoding_rs::EUC_KR),
852            (195, encoding_rs::EUC_KR),
853            (196, encoding_rs::BIG5),
854            (197, encoding_rs::BIG5),
855            (202, encoding_rs::BIG5),
856            (198, encoding_rs::GB18030),
857            (199, encoding_rs::GB18030),
858            (203, encoding_rs::GB18030),
859            (201, encoding_rs::BIG5),
860            (204, encoding_rs::WINDOWS_874),
861            (205, encoding_rs::WINDOWS_874),
862            (206, encoding_rs::WINDOWS_874),
863            (210, encoding_rs::WINDOWS_1252),
864            (211, encoding_rs::WINDOWS_1252),
865            (212, encoding_rs::WINDOWS_1252),
866            (213, encoding_rs::WINDOWS_1252),
867            (214, encoding_rs::WINDOWS_1252),
868            (215, encoding_rs::WINDOWS_1252),
869            (216, encoding_rs::WINDOWS_1252),
870            (217, encoding_rs::WINDOWS_1252),
871        ];
872
873        for (sort_id, expected) in cases {
874            assert_eq!(
875                sortid_to_encoding(*sort_id),
876                Some(*expected),
877                "sort_id {}",
878                sort_id
879            );
880        }
881    }
882
883    #[test]
884    fn derives_eq_and_copy() {
885        let a = Collation::new(0x0409, 0);
886        let b = a;
887        assert_eq!(a, b);
888        assert_ne!(a, Collation::new(0x0409, 1));
889    }
890}