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