/src/adhd/cras/server/ini/src/utf8cstring.rs
Line | Count | Source |
1 | | // Copyright 2024 The ChromiumOS Authors |
2 | | // Use of this source code is governed by a BSD-style license that can be |
3 | | // found in the LICENSE file. |
4 | | |
5 | | use std::borrow::Borrow; |
6 | | use std::ffi::CStr; |
7 | | use std::hash::Hash; |
8 | | |
9 | | /// A Null-terminated UTF-8 string that can be borrowed as `&str` and `&cstr`. |
10 | | #[derive(PartialEq, Eq)] |
11 | | pub struct Utf8CString { |
12 | | // Invariant: data must terminated with a single NULL. |
13 | | data: String, |
14 | | } |
15 | | |
16 | | impl TryFrom<String> for Utf8CString { |
17 | | type Error = anyhow::Error; |
18 | | |
19 | 0 | fn try_from(mut value: String) -> Result<Self, Self::Error> { |
20 | 0 | value.push('\0'); |
21 | 0 | CStr::from_bytes_with_nul(value.as_bytes())?; |
22 | 0 | Ok(Self { data: value }) |
23 | 0 | } |
24 | | } |
25 | | |
26 | | impl Utf8CString { |
27 | | /// Return the contained string as a &str. |
28 | 0 | pub fn as_str(&self) -> &str { |
29 | 0 | self.data.strip_suffix('\0').unwrap() |
30 | 0 | } |
31 | | |
32 | | /// Return the contained string as a &CStr. |
33 | 0 | pub fn as_cstr(&self) -> &CStr { |
34 | 0 | CStr::from_bytes_with_nul(self.data.as_bytes()).unwrap() |
35 | 0 | } |
36 | | } |
37 | | |
38 | | impl Borrow<str> for Utf8CString { |
39 | 0 | fn borrow(&self) -> &str { |
40 | 0 | self.as_str() |
41 | 0 | } |
42 | | } |
43 | | |
44 | | impl Hash for Utf8CString { |
45 | 0 | fn hash<H: std::hash::Hasher>(&self, state: &mut H) { |
46 | 0 | self.as_str().hash(state); |
47 | 0 | } |
48 | | } |
49 | | |
50 | | #[cfg(test)] |
51 | | mod tests { |
52 | | use super::*; |
53 | | |
54 | | #[test] |
55 | | fn has_null() { |
56 | | assert!(Utf8CString::try_from(String::from("hello\0world")).is_err()); |
57 | | assert!(Utf8CString::try_from(String::from("hello world\0")).is_err()); |
58 | | } |
59 | | |
60 | | #[test] |
61 | | fn roundtrip_str() { |
62 | | let s = "hello world"; |
63 | | assert_eq!(Utf8CString::try_from(s.to_string()).unwrap().as_str(), s); |
64 | | } |
65 | | |
66 | | #[test] |
67 | | fn roundtrip_cstr() { |
68 | | let s = "hello world"; |
69 | | assert_eq!( |
70 | | Utf8CString::try_from(s.to_string()).unwrap().as_cstr(), |
71 | | c"hello world" |
72 | | ); |
73 | | } |
74 | | } |