1use fidl_fuchsia_io as fio;
6use fidl_fuchsia_starnix_binder as fbinder;
7use starnix_uapi::open_flags::OpenFlags;
8use std::ops::Bound;
9
10pub trait FromFidl<T>: Sized {
11 fn from_fidl(value: T) -> Self;
12}
13
14pub trait IntoFidl<T>: Sized {
15 fn into_fidl(self) -> T;
17}
18
19impl<T, U> IntoFidl<U> for T
20where
21 U: FromFidl<T>,
22{
23 fn into_fidl(self) -> U {
24 U::from_fidl(self)
25 }
26}
27
28impl FromFidl<fio::OpenFlags> for OpenFlags {
29 fn from_fidl(fio_flags: fio::OpenFlags) -> Self {
30 let mut result = if fio_flags.contains(fio::OpenFlags::RIGHT_WRITABLE) {
31 if fio_flags.contains(fio::OpenFlags::RIGHT_READABLE) {
32 OpenFlags::RDWR
33 } else {
34 OpenFlags::WRONLY
35 }
36 } else {
37 OpenFlags::RDONLY
38 };
39 if fio_flags.contains(fio::OpenFlags::CREATE) {
40 result |= OpenFlags::CREAT;
41 }
42 if fio_flags.contains(fio::OpenFlags::CREATE_IF_ABSENT) {
43 result |= OpenFlags::EXCL;
44 }
45 if fio_flags.contains(fio::OpenFlags::TRUNCATE) {
46 result |= OpenFlags::TRUNC;
47 }
48 if fio_flags.contains(fio::OpenFlags::APPEND) {
49 result |= OpenFlags::APPEND;
50 }
51 if fio_flags.contains(fio::OpenFlags::DIRECTORY) {
52 result |= OpenFlags::DIRECTORY;
53 }
54 result
55 }
56}
57
58impl FromFidl<OpenFlags> for fio::OpenFlags {
59 fn from_fidl(flags: OpenFlags) -> fio::OpenFlags {
60 let mut result = fio::OpenFlags::empty();
61 if flags.can_read() {
62 result |= fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::POSIX_WRITABLE;
63 }
64 if flags.can_write() {
65 result |= fio::OpenFlags::RIGHT_WRITABLE;
66 }
67 if flags.contains(OpenFlags::CREAT) {
68 result |= fio::OpenFlags::CREATE;
69 }
70 if flags.contains(OpenFlags::EXCL) {
71 result |= fio::OpenFlags::CREATE_IF_ABSENT;
72 }
73 if flags.contains(OpenFlags::TRUNC) {
74 result |= fio::OpenFlags::TRUNCATE;
75 }
76 if flags.contains(OpenFlags::APPEND) {
77 result |= fio::OpenFlags::APPEND;
78 }
79 if flags.contains(OpenFlags::DIRECTORY) {
80 result |= fio::OpenFlags::DIRECTORY;
81 }
82 result
83 }
84}
85
86impl FromFidl<OpenFlags> for fbinder::FileFlags {
87 fn from_fidl(flags: OpenFlags) -> Self {
88 let mut result = Self::empty();
89 if flags.can_read() {
90 result |= Self::RIGHT_READABLE;
91 }
92 if flags.can_write() {
93 result |= Self::RIGHT_WRITABLE;
94 }
95 if flags.contains(OpenFlags::DIRECTORY) {
96 result |= Self::DIRECTORY;
97 }
98
99 result
100 }
101}
102
103impl FromFidl<fbinder::FileFlags> for OpenFlags {
104 fn from_fidl(flags: fbinder::FileFlags) -> Self {
105 let readable = flags.contains(fbinder::FileFlags::RIGHT_READABLE);
106 let writable = flags.contains(fbinder::FileFlags::RIGHT_WRITABLE);
107 let mut result = Self::empty();
108 if readable && writable {
109 result = Self::RDWR;
110 } else if writable {
111 result = Self::WRONLY;
112 } else if readable {
113 result = Self::RDONLY;
114 }
115 if flags.contains(fbinder::FileFlags::DIRECTORY) {
116 result |= Self::DIRECTORY;
117 }
118 result
119 }
120}
121
122pub trait BoundExt<T> {
129 fn try_map<U, E, F>(self, f: F) -> Result<Bound<U>, E>
130 where
131 F: FnOnce(T) -> Result<U, E>;
132}
133
134impl<T> BoundExt<T> for Bound<T> {
135 fn try_map<U, E, F>(self, f: F) -> Result<Bound<U>, E>
136 where
137 F: FnOnce(T) -> Result<U, E>,
138 {
139 match self {
140 Bound::Unbounded => Ok(Bound::Unbounded),
141 Bound::Included(x) => Ok(Bound::Included(f(x)?)),
142 Bound::Excluded(x) => Ok(Bound::Excluded(f(x)?)),
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use starnix_uapi::uapi;
151
152 fn assert_flags_equals(flags: OpenFlags, fio_flags: fio::OpenFlags) {
153 assert_eq!(flags, fio_flags.into_fidl());
154 assert_eq!(fio_flags, flags.into_fidl());
155 }
156
157 #[::fuchsia::test]
158 fn test_access() {
159 let read_only = OpenFlags::from_bits_truncate(uapi::O_RDONLY);
160 assert!(read_only.can_read());
161 assert!(!read_only.can_write());
162
163 let write_only = OpenFlags::from_bits_truncate(uapi::O_WRONLY);
164 assert!(!write_only.can_read());
165 assert!(write_only.can_write());
166
167 let read_write = OpenFlags::from_bits_truncate(uapi::O_RDWR);
168 assert!(read_write.can_read());
169 assert!(read_write.can_write());
170 }
171
172 #[::fuchsia::test]
173 fn test_conversion() {
174 assert_flags_equals(
175 OpenFlags::RDONLY,
176 fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::POSIX_WRITABLE,
177 );
178 assert_flags_equals(OpenFlags::WRONLY, fio::OpenFlags::RIGHT_WRITABLE);
179 assert_flags_equals(
180 OpenFlags::RDWR,
181 fio::OpenFlags::RIGHT_READABLE
182 | fio::OpenFlags::RIGHT_WRITABLE
183 | fio::OpenFlags::POSIX_WRITABLE,
184 );
185 }
186
187 #[::fuchsia::test]
188 fn test_bound_try_map() {
189 let f = |x: usize| -> Result<std::num::NonZeroU16, &'static str> {
190 let val = u16::try_from(x).map_err(|_| "out of range")?;
191 std::num::NonZeroU16::new(val).ok_or("is zero")
192 };
193
194 assert_eq!(
195 Bound::<usize>::Unbounded.try_map(f),
196 Ok(Bound::<std::num::NonZeroU16>::Unbounded)
197 );
198 assert_eq!(
199 Bound::Included(5usize).try_map(f),
200 Ok(Bound::Included(std::num::NonZeroU16::new(5).unwrap()))
201 );
202 assert_eq!(
203 Bound::Excluded(3usize).try_map(f),
204 Ok(Bound::Excluded(std::num::NonZeroU16::new(3).unwrap()))
205 );
206
207 assert_eq!(Bound::Included(usize::MAX).try_map(f), Err("out of range"));
208 assert_eq!(Bound::Included(0usize).try_map(f), Err("is zero"));
209 }
210}