1#[cfg(test)]
2use anyhow::bail;
3use std::ops::Deref;
4
5#[derive(thiserror::Error, Debug)]
6pub(crate) enum Error {
7 #[error("input string is not ASCII: {0}")]
8 InvalidAscii(String),
9}
10
11#[derive(Clone, PartialEq)]
13pub(crate) struct AsciiString {
14 value: String,
15}
16
17impl AsciiString {
18 pub fn new(value: String) -> Result<AsciiString, Error> {
19 if value.is_ascii() {
20 Ok(Self { value })
21 } else {
22 Err(Error::InvalidAscii(value))
23 }
24 }
25}
26
27impl Deref for AsciiString {
28 type Target = String;
29
30 fn deref(&self) -> &Self::Target {
31 &self.value
32 }
33}
34
35impl std::fmt::Debug for AsciiString {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 self.value.fmt(f)
38 }
39}
40
41impl std::fmt::Display for AsciiString {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 self.value.fmt(f)
44 }
45}
46
47#[test]
48fn test_ascii_string() -> anyhow::Result<()> {
49 let valid_ascii: [String; 8] = [
50 "".to_string(),
51 " !\"#$%&'()*+,-./".to_string(),
52 "0123456789".to_string(),
53 ":<=>?@".to_string(),
54 "ABCDEFGHIJKLMNOPQRSTUVWXYZ".to_string(),
55 "[\\]^_`".to_string(),
56 "abcdefghijklmnopqstuvwxyz".to_string(),
57 "{|}~".to_string(),
58 ];
59
60 for string in valid_ascii {
61 match AsciiString::new(string) {
62 Ok(string) => println!("ascii: '{}'", string),
63 Err(err) => bail!("unexpected error: {}", err),
64 }
65 }
66
67 let utf8: [String; 2] = ["❤".to_string(), "heart ❤".to_string()];
68
69 for string in utf8 {
70 match AsciiString::new(string) {
71 Ok(string) => bail!("this is not ascii: {}", string),
72 Err(_) => (),
73 }
74 }
75
76 Ok(())
77}