not_io/
lib.rs

1//! Provides `Read` and `Write` alternatives on `no_std` while being compatible with the full
2//! traits from `std` when allowed.
3//!
4//! ## Motivation
5//!
6//! The file parser ecosystem of Rust is more or less split across crates that use `no_std` and
7//! crates that do not, as well as between crates using `alloc` and no-alloc (and the largely
8//! overlapping zero-copy) crates. This has several reasons:
9//!
10//! * The `std::io::Read` and `std::io::Write` traits require an allocator due to their internal
11//!   implementation and were not written to be OS independent.
12//! * Before `1.36` it was not possible to depend on `alloc` without `std`.
13//! * The lack of specialization makes it hard to be both generic over implementors of the standard
14//!   traits while still allowing use when those traits are not available. This is in particular
15//!   also since several types (e.g. `&[u8]`) implement those traits but would obviously be useful
16//!   as byte sources and sinks even when they are unavailable.
17//!
18//! ## Usage guide
19//!
20//! This crate assumes you have a structure declared roughly as follows:
21//!
22//! ```rust
23//! # struct SomeItem;
24//! # use std::io::Read;
25//!
26//! struct Decoder<T> {
27//!     reader: T,
28//! }
29//!
30//! impl<T: std::io::Read> Decoder<T> {
31//!     fn next(&mut self) -> Result<SomeItem, std::io::Error> {
32//!         let mut buffer = vec![];
33//!         self.reader.read_to_end(&mut buffer)?;
34//! # unimplemented!()
35//!     }
36//! }
37//! ```
38//!
39//! There is only one necessary change, be sure to keep the `std` feature enabled for now. This
40//! should not break any code except if you relied on the precise type `T` in which case you will
41//! need to use a few derefs and/or `into_inner`.
42//!
43//! ```
44//! use not_io::AllowStd;
45//! # use std::io::Read;
46//!
47//! struct Decoder<T> {
48//!     reader: AllowStd<T>,
49//! }
50//!
51//! # struct SomeItem;
52//! # impl<T: std::io::Read> Decoder<T> {
53//! #    fn next(&mut self) -> Result<SomeItem, std::io::Error> {
54//! #        let mut buffer = vec![];
55//! #        self.reader.0.read_to_end(&mut buffer)?;
56//! # unimplemented!()
57//! #    }
58//! # }
59//! ```
60//!
61//! And finally you can add to your crate a new default feature which enables the `std`/`alloc`
62//! feature of this crate, and conditionally active your existing interfaces only when that feature
63//! is active. Then add a few new impls that can be used even when the feature is inactive.
64//!
65//! ```
66//! use not_io::AllowStd;
67//! # struct SomeItem;
68//!
69//! struct Decoder<T> {
70//!     reader: AllowStd<T>,
71//! }
72//!
73//! /// The interface which lets the caller select which feature to turn on.
74//! impl<T> Decoder<T>
75//! where
76//!     AllowStd<T>: not_io::Read
77//! {
78//!     fn no_std_next(&mut self) -> Result<SomeItem, not_io::Error> {
79//! # unimplemented!()
80//!     }
81//! }
82//!
83//! /// An interface for pure no_std use with caller provide no_std reader.
84//! impl<T> Decoder<T>
85//! where
86//!     T: not_io::Read
87//! {
88//!     fn not_io_next(&mut self) -> Result<SomeItem, not_io::Error> {
89//!         let reader = &mut self.reader.0;
90//! # unimplemented!()
91//!     }
92//! }
93//! ```
94//!
95#![cfg_attr(not(feature = "std"), no_std)]
96
97#[cfg(all(feature = "alloc", not(feature = "std")))]
98extern crate alloc;
99
100pub struct Error {
101    _private: (),
102}
103
104pub type Result<T> = core::result::Result<T, Error>;
105
106pub trait Read {
107    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
108}
109
110pub trait Write {
111    fn write(&mut self, buf: &[u8]) -> Result<usize>;
112}
113
114/// A simple new type wrapper holding a potential reader or writer.
115///
116/// This type allows the library to satisfy the compatibility across different features without
117/// having to resort to specialization. Simply put, this struct implements `Read` and `Write`:
118///
119/// * for all types that implement the respective trait from `std` if the `std` feature is active.
120/// * on a concrete subset of those types if the `alloc` feature but not the `std` feature has been
121///   turned on.
122/// * only for types from `core` when neither feature is turned on.
123///
124/// Note that without this type we couldn't safely introduce a conditionally active, generic impl
125/// of our own traits. The reason is that features must only activate SemVer compatible changes.
126/// These two sets of impls are not SemVer compatible due to the uncovered generic `T`. In
127/// particular in the first case you'd be allowed to implement the trait for your own type that
128/// also implements `std::io::Read` while in the second this is an impl conflict.
129///
130/// * `impl Read for &'_ [u8]`
131/// * `impl<T> Read for T where std::io::Read`
132///
133/// By adding our own private struct as a layer of indirection, you are no longer allowed to make
134/// such changes:
135///
136/// * `impl Read for AllowStd<&'_ [u8]>`
137/// * `impl<T> Read for AllowStd<T> where T: std::io::Read`
138///
139/// This still means there is one impl which will never be added. Instead, the impls for
140/// core/standard types are provided separately and individually.
141///
142/// * `impl<T> Read for AllowStd<T> where T: crate::Read`
143pub struct AllowStd<T>(pub T);
144
145#[cfg(not(feature = "alloc"))]
146mod impls_on_neither {}
147
148#[cfg(feature = "alloc")]
149mod impls_on_alloc {}
150
151#[cfg(feature = "std")]
152mod impls_on_std {
153    use super::{AllowStd, Error, Result};
154    use std::io::{self, IoSlice, IoSliceMut};
155
156    impl<R: io::Read> super::Read for AllowStd<R> {
157        fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
158            io::Read::read(&mut self.0, buf).map_err(Error::from)
159        }
160    }
161
162    impl<R: io::Read> io::Read for AllowStd<R> {
163        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
164            self.0.read(buf)
165        }
166        fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
167            self.0.read_vectored(bufs)
168        }
169        fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
170            self.0.read_to_end(buf)
171        }
172        fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
173            self.0.read_to_string(buf)
174        }
175        fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
176            self.0.read_exact(buf)
177        }
178    }
179
180    impl<W: io::Write> super::Write for AllowStd<W> {
181        fn write(&mut self, buf: &[u8]) -> Result<usize> {
182            io::Write::write(&mut self.0, buf).map_err(Error::from)
183        }
184    }
185
186    impl<W: io::Write> io::Write for AllowStd<W> {
187        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
188            self.0.write(buf)
189        }
190        fn flush(&mut self) -> io::Result<()> {
191            self.0.flush()
192        }
193        fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result<usize> {
194            self.0.write_vectored(bufs)
195        }
196        fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
197            self.0.write_all(buf)
198        }
199    }
200
201    impl From<io::Error> for Error {
202        fn from(_: io::Error) -> Error {
203            Error { _private: () }
204        }
205    }
206}