Skip to main content

io_enum/
lib.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3/*!
4<!-- Note: Document from sync-markdown-to-rustdoc:start through sync-markdown-to-rustdoc:end
5     is synchronized from README.md. Any changes to that range are not preserved. -->
6<!-- tidy:sync-markdown-to-rustdoc:start -->
7
8\#\[derive(Read, Write, Seek, BufRead)\] for enums.
9
10## Usage
11
12Add this to your `Cargo.toml`:
13
14```toml
15[dependencies]
16io-enum = "1"
17```
18
19## Examples
20
21```
22use std::{
23    fs::File,
24    io::{self, Write},
25    path::Path,
26};
27
28use io_enum::{Read, Write, Seek, BufRead};
29
30#[derive(Read, Write, Seek, BufRead)]
31enum Either<A, B> {
32    A(A),
33    B(B),
34}
35
36fn func(path: Option<&Path>) -> impl Write {
37    if let Some(path) = path {
38        Either::A(File::open(path).unwrap())
39    } else {
40        Either::B(io::stdout())
41    }
42}
43```
44
45See [auto_enums] crate for how to automate patterns like this.
46
47## Supported traits
48
49- [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) - [example](https://github.com/taiki-e/io-enum/blob/HEAD/tests/expand/read.rs) | [generated code](https://github.com/taiki-e/io-enum/blob/HEAD/tests/expand/read.expanded.rs)
50- [`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html) - [example](https://github.com/taiki-e/io-enum/blob/HEAD/tests/expand/buf_read.rs) | [generated code](https://github.com/taiki-e/io-enum/blob/HEAD/tests/expand/buf_read.expanded.rs)
51- [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) - [example](https://github.com/taiki-e/io-enum/blob/HEAD/tests/expand/write.rs) | [generated code](https://github.com/taiki-e/io-enum/blob/HEAD/tests/expand/write.expanded.rs)
52- [`Seek`](https://doc.rust-lang.org/std/io/trait.Seek.html) - [example](https://github.com/taiki-e/io-enum/blob/HEAD/tests/expand/seek.rs) | [generated code](https://github.com/taiki-e/io-enum/blob/HEAD/tests/expand/seek.expanded.rs)
53
54## Related Projects
55
56- [auto_enums]: A library for to allow multiple return types by automatically generated enum.
57- [derive_utils]: A procedural macro helper for easily writing [derives macros][proc-macro-derive] for enums.
58- [iter-enum]: \#\[derive(Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator, Extend)\] for enums.
59
60[auto_enums]: https://github.com/taiki-e/auto_enums
61[derive_utils]: https://github.com/taiki-e/derive_utils
62[iter-enum]: https://github.com/taiki-e/iter-enum
63[proc-macro-derive]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros
64
65<!-- tidy:sync-markdown-to-rustdoc:end -->
66*/
67
68#![doc(test(
69    no_crate_inject,
70    attr(allow(
71        dead_code,
72        unused_variables,
73        clippy::undocumented_unsafe_blocks,
74        clippy::unused_trait_names,
75    ))
76))]
77#![forbid(unsafe_code)]
78
79use derive_utils::quick_derive;
80use proc_macro::TokenStream;
81
82#[proc_macro_derive(Read)]
83pub fn derive_read(input: TokenStream) -> TokenStream {
84    // TODO: Add is_read_vectored once stabilized https://github.com/rust-lang/rust/issues/69941
85    // TODO: Add read_buf,read_buf_exact once stabilized https://github.com/rust-lang/rust/issues/78485
86    quick_derive! {
87        input,
88        ::std::io::Read,
89        trait Read {
90            #[inline]
91            fn read(&mut self, buf: &mut [u8]) -> ::std::io::Result<usize>;
92            #[inline]
93            fn read_vectored(
94                &mut self, bufs: &mut [::std::io::IoSliceMut<'_>],
95            ) -> ::std::io::Result<usize>;
96            #[inline]
97            fn read_to_end(&mut self, buf: &mut ::std::vec::Vec<u8>) -> ::std::io::Result<usize>;
98            #[inline]
99            fn read_to_string(
100                &mut self,
101                buf: &mut ::std::string::String,
102            ) -> ::std::io::Result<usize>;
103            #[inline]
104            fn read_exact(&mut self, buf: &mut [u8]) -> ::std::io::Result<()>;
105        }
106    }
107}
108
109#[proc_macro_derive(Write)]
110pub fn derive_write(input: TokenStream) -> TokenStream {
111    // TODO: Add is_write_vectored once stabilized https://github.com/rust-lang/rust/issues/69941
112    // TODO: Add write_all_vectored once stabilized https://github.com/rust-lang/rust/issues/70436
113    quick_derive! {
114        input,
115        ::std::io::Write,
116        trait Write {
117            #[inline]
118            fn write(&mut self, buf: &[u8]) -> ::std::io::Result<usize>;
119            #[inline]
120            fn write_vectored(
121                &mut self,
122                bufs: &[::std::io::IoSlice<'_>],
123            ) -> ::std::io::Result<usize>;
124            #[inline]
125            fn flush(&mut self) -> ::std::io::Result<()>;
126            #[inline]
127            fn write_all(&mut self, buf: &[u8]) -> ::std::io::Result<()>;
128            #[inline]
129            fn write_fmt(&mut self, fmt: ::std::fmt::Arguments<'_>) -> ::std::io::Result<()>;
130        }
131    }
132}
133
134#[proc_macro_derive(Seek)]
135pub fn derive_seek(input: TokenStream) -> TokenStream {
136    quick_derive! {
137        input,
138        ::std::io::Seek,
139        trait Seek {
140            #[inline]
141            fn seek(&mut self, pos: ::std::io::SeekFrom) -> ::std::io::Result<u64>;
142        }
143    }
144}
145
146#[proc_macro_derive(BufRead)]
147pub fn derive_buf_read(input: TokenStream) -> TokenStream {
148    quick_derive! {
149        input,
150        ::std::io::BufRead,
151        trait BufRead {
152            #[inline]
153            fn fill_buf(&mut self) -> ::std::io::Result<&[u8]>;
154            #[inline]
155            fn consume(&mut self, amt: usize);
156            #[inline]
157            fn read_until(
158                &mut self, byte: u8, buf: &mut ::std::vec::Vec<u8>,
159            ) -> ::std::io::Result<usize>;
160            #[inline]
161            fn read_line(&mut self, buf: &mut ::std::string::String) -> ::std::io::Result<usize>;
162        }
163    }
164}