1use std::fmt;
2use std::path::Path;
3use std::str::FromStr;
4
5use once_cell::sync::Lazy;
6use regex::Regex;
7
8use crate::common::{Codepoint, CodepointIter, UcdFile, UcdFileByCodepoint};
9use crate::error::Error;
10
11/// Represents a single row in the `BidiMirroring.txt` file.
12///
13/// The field names were taken from the header of BidiMirroring.txt.
14#[derive(Clone, Debug, Default, Eq, PartialEq)]
15pub struct BidiMirroring {
16 /// The codepoint corresponding to this row.
17 pub codepoint: Codepoint,
18 /// The codepoint that has typically has a glyph that is the mirror image
19 /// of `codepoint`.
20 pub bidi_mirroring_glyph: Codepoint,
21}
22
23impl UcdFile for BidiMirroring {
24 fn relative_file_path() -> &'static Path {
25 Path::new("BidiMirroring.txt")
26 }
27}
28
29impl UcdFileByCodepoint for BidiMirroring {
30 fn codepoints(&self) -> CodepointIter {
31 self.codepoint.into_iter()
32 }
33}
34
35impl FromStr for BidiMirroring {
36 type Err = Error;
37
38 fn from_str(line: &str) -> Result<BidiMirroring, Error> {
39 static PARTS: Lazy<Regex> = Lazy::new(|| {
40 Regex::new(
41 r"(?x)
42 ^
43 \s*(?P<codepoint>[A-F0-9]+)\s*;
44 \s*(?P<substitute_codepoint>[A-F0-9]+)
45 \s+
46 \#(?:.+)
47 $
48 ",
49 )
50 .unwrap()
51 });
52 let caps = match PARTS.captures(line.trim()) {
53 Some(caps) => caps,
54 None => return err!("invalid BidiMirroring line"),
55 };
56
57 Ok(BidiMirroring {
58 codepoint: caps["codepoint"].parse()?,
59 bidi_mirroring_glyph: caps["substitute_codepoint"].parse()?,
60 })
61 }
62}
63
64impl fmt::Display for BidiMirroring {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(f, "{};", self.codepoint)?;
67 write!(f, "{};", self.bidi_mirroring_glyph)?;
68 Ok(())
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use crate::common::Codepoint;
75
76 use super::BidiMirroring;
77
78 fn codepoint(n: u32) -> Codepoint {
79 Codepoint::from_u32(n).unwrap()
80 }
81
82 #[test]
83 fn parse() {
84 let line = "0028; 0029 # LEFT PARENTHESIS\n";
85 let data: BidiMirroring = line.parse().unwrap();
86 assert_eq!(
87 data,
88 BidiMirroring {
89 codepoint: codepoint(0x0028),
90 bidi_mirroring_glyph: codepoint(0x0029),
91 }
92 );
93 }
94
95 #[test]
96 fn parse_best_fit() {
97 let line = "228A; 228B # [BEST FIT] SUBSET OF WITH NOT EQUAL TO\n";
98 let data: BidiMirroring = line.parse().unwrap();
99 assert_eq!(
100 data,
101 BidiMirroring {
102 codepoint: codepoint(0x228A),
103 bidi_mirroring_glyph: codepoint(0x228B),
104 }
105 );
106 }
107}
108