Skip to main content

starnix_types/
convert.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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    // Required method
16    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
122/// Extension trait for `std::ops::Bound` to allow fallible mapping of its contents.
123///
124/// This is useful in situations where range bounds must be converted into a different type
125/// prior to range-queries (e.g. converting a `Bound<usize>` received from an API but needing
126/// to index into an internal structure requiring `Bound<std::num::NonZeroU16>`), where
127/// the conversion operation itself is fallible (e.g. `try_from`).
128pub 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}