1use std::{
2 borrow::Borrow,
3 ffi::{c_char, CStr},
4 fmt,
5 ptr::null,
6 str::FromStr,
7};
8
9use crate::{sys, AllocationError};
10
11#[derive(Clone, Copy)]
16pub struct Tag(u32);
17
18impl Tag {
19 pub fn new(tag: impl Borrow<[u8; 4]>) -> Self {
33 Self(u32::from_be_bytes(*tag.borrow()))
34 }
35}
36
37impl From<Tag> for u32 {
38 fn from(tag: Tag) -> Self {
39 tag.0
40 }
41}
42
43impl From<u32> for Tag {
44 fn from(tag: u32) -> Self {
45 Self(tag)
46 }
47}
48
49impl From<Tag> for [u8; 4] {
50 fn from(tag: Tag) -> Self {
51 tag.0.to_be_bytes()
52 }
53}
54
55impl fmt::Debug for Tag {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 struct FieldFormatter([u8; 4]);
58 impl fmt::Debug for FieldFormatter {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(
61 f,
62 "{}{}{}{}",
63 self.0[0] as char, self.0[1] as char, self.0[2] as char, self.0[3] as char
64 )
65 }
66 }
67 f.debug_tuple("Tag")
68 .field(&FieldFormatter((*self).into()))
69 .finish()
70 }
71}
72
73#[derive(Clone, Copy)]
77pub struct Language(sys::hb_language_t);
78
79impl Language {
80 pub fn as_raw(&self) -> sys::hb_language_t {
82 self.0
83 }
84
85 pub unsafe fn from_raw(lang: sys::hb_language_t) -> Self {
91 Self(lang)
92 }
93}
94
95impl Default for Language {
96 fn default() -> Self {
97 Self(null())
98 }
99}
100
101impl FromStr for Language {
102 type Err = AllocationError;
103
104 fn from_str(s: &str) -> Result<Self, Self::Err> {
105 if s.is_empty() {
106 return Ok(Self(sys::HB_LANGUAGE_INVALID));
107 }
108 let lang =
109 unsafe { sys::hb_language_from_string(s.as_ptr() as *const c_char, s.len() as i32) };
110 if lang.is_null() {
111 return Err(AllocationError);
112 }
113 Ok(Self(lang))
114 }
115}
116
117impl fmt::Display for Language {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 let lang = unsafe { sys::hb_language_to_string(self.0) };
120 if lang.is_null() {
121 return write!(f, "[invalid]");
122 }
123 let lang = unsafe { CStr::from_ptr(lang) };
124 if let Ok(lang) = lang.to_str() {
125 write!(f, "{lang}")
126 } else {
127 write!(f, "[invalid]")
128 }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn tag_debug_is_correct() {
138 assert_eq!(u32::from(Tag::new([b'D', b'S', b'I', b'G'])), 0x44534947u32);
139 assert_eq!(format!("{:?}", Tag::new([b'D', b'S', b'I', b'G'])), "Tag(DSIG)");
140 assert_eq!(format!("{:?}", Tag::new(b"DSIG")), "Tag(DSIG)");
141 }
142
143 #[test]
144 fn language_works() {
145 assert_eq!(Language::from_str("").unwrap().to_string(), "[invalid]");
146 assert_eq!(Language::from_str("en").unwrap().to_string(), "en");
147 assert_eq!(Language::from_str("non-existent").unwrap().to_string(), "non-existent");
148 }
149}