robius_authentication/text.rs
1/// The text contents displayed by an authentication prompt.
2pub struct Text<'a, 'b, 'c, 'd, 'e, 'f> {
3 /// The text of the authentication prompt on Android.
4 pub android: AndroidText<'a, 'b, 'c>,
5 /// The description of the authentication prompt on Apple devices.
6 ///
7 /// Appears as "$(binary_name) is trying to $(description)".
8 pub apple: &'d str,
9 /// The description of the authentication prompt on Windows.
10 pub windows: WindowsText<'e, 'f>,
11}
12
13/// The text of the authentication prompt on Android.
14pub struct AndroidText<'a, 'b, 'c> {
15 pub title: &'a str,
16 pub subtitle: Option<&'b str>,
17 pub description: Option<&'c str>,
18}
19
20/// The text of the authentication prompt on Windows,
21/// including a title ("caption") and description ("message").
22pub struct WindowsText<'a, 'b> {
23 #[allow(dead_code)]
24 pub(crate) title: &'a str,
25 #[allow(dead_code)]
26 pub(crate) description: &'b str,
27}
28
29impl<'a, 'b> WindowsText<'a, 'b> {
30 /// Creates a new `WindowsText` instance.
31 ///
32 /// Returns `None` if `title` exceeds 128 bytes in length
33 /// or if `description` exceeds 1024 bytes in length.
34 #[cfg(target_os = "windows")]
35 pub const fn new(title: &'a str, description: &'b str) -> Option<Self> {
36 use windows::Win32::Security::Credentials::{
37 CREDUI_MAX_CAPTION_LENGTH, CREDUI_MAX_MESSAGE_LENGTH,
38 };
39
40 if title.len() <= CREDUI_MAX_CAPTION_LENGTH as usize
41 && description.len() <= CREDUI_MAX_MESSAGE_LENGTH as usize
42 {
43 Some(Self { title, description })
44 } else {
45 None
46 }
47 }
48
49 /// Creates a new `WindowsText` instance.
50 ///
51 /// On Windows, returns `None` if `title` exceeds 128 bytes in length
52 /// or if `description` exceeds 1024 bytes in length. On other targets the
53 /// text is unused, so no validation is performed and this always returns
54 /// `Some`.
55 #[cfg(not(target_os = "windows"))]
56 pub const fn new(title: &'a str, description: &'b str) -> Option<Self> {
57 Some(Self { title, description })
58 }
59
60 /// Creates a new `WindowsText` instance.
61 ///
62 /// The `title` ("caption") will be truncated to at most 128 bytes in length,
63 /// and the `description` ("message") will be truncated to at most 1024 bytes in length.
64 /// Truncation respects UTF-8 character boundaries, so fewer bytes may be kept.
65 #[cfg(target_os = "windows")]
66 pub fn new_truncated(title: &'a str, description: &'b str) -> Self {
67 use windows::Win32::Security::Credentials::{
68 CREDUI_MAX_CAPTION_LENGTH, CREDUI_MAX_MESSAGE_LENGTH,
69 };
70
71 Self {
72 title: truncate_to_char_boundary(title, CREDUI_MAX_CAPTION_LENGTH as usize),
73 description: truncate_to_char_boundary(
74 description,
75 CREDUI_MAX_MESSAGE_LENGTH as usize,
76 ),
77 }
78 }
79
80 /// Creates a new `WindowsText` instance.
81 ///
82 /// On Windows, the `title` ("caption") is truncated to at most 128 bytes and
83 /// the `description` ("message") to at most 1024 bytes, respecting UTF-8
84 /// character boundaries. On other targets the text is unused and is stored
85 /// verbatim without truncation.
86 #[cfg(not(target_os = "windows"))]
87 pub fn new_truncated(title: &'a str, description: &'b str) -> Self {
88 Self { title, description }
89 }
90}
91
92/// Truncates `s` to at most `max_len` bytes without splitting a UTF-8 character.
93#[cfg(target_os = "windows")]
94fn truncate_to_char_boundary(s: &str, max_len: usize) -> &str {
95 if s.len() <= max_len {
96 return s;
97 }
98 let mut end = max_len;
99 while !s.is_char_boundary(end) {
100 end -= 1;
101 }
102 &s[..end]
103}