shellfn_core/execute/
iter.rs

1use crate::error::Error;
2use crate::utils::{spawn, PANIC_MSG};
3use itertools::Either;
4use std::error::Error as StdError;
5use std::ffi::OsStr;
6use std::io::{BufRead, BufReader};
7use std::str::FromStr;
8
9/// Executes command with args and environment variables, parses output line by line
10/// * On invalid command: return error
11/// * On error exit code: break iterator
12/// * On parsing failure: yield error item
13/// * Possible errors: ProcessNotSpawned, StdoutUnreadable (item error), ParsingError (item error)
14///
15/// Designed for
16/// ```rust
17/// use shellfn::shell;
18/// use std::error::Error;
19///
20/// #[shell]
21/// fn command() -> Result<impl Iterator<Item = Result<u32, Box<Error + 'static>>>, Box<Error>> {
22///     "echo 1; echo 2; echo 3"
23/// }
24///
25/// assert_eq!(vec![1, 2, 3], command().unwrap().map(Result::unwrap).collect::<Vec<_>>())
26/// ```
27pub fn execute_iter_result_result<T, TArg, TEnvKey, TEnvVal, TOuterError, TInnerError>(
28    cmd: impl AsRef<OsStr>,
29    args: impl IntoIterator<Item = TArg>,
30    envs: impl IntoIterator<Item = (TEnvKey, TEnvVal)>,
31) -> Result<impl Iterator<Item = Result<T, TInnerError>>, TOuterError>
32where
33    T: FromStr,
34    TArg: AsRef<OsStr>,
35    TEnvKey: AsRef<OsStr>,
36    TEnvVal: AsRef<OsStr>,
37    <T as FromStr>::Err: StdError,
38    TOuterError: From<Error<<T as FromStr>::Err>>,
39    TInnerError: From<Error<<T as FromStr>::Err>>,
40{
41    let mut process = spawn(cmd, args, envs).map_err(Error::ProcessNotSpawned)?;
42    let stdout = process.stdout.take().unwrap();
43
44    Ok(BufReader::new(stdout).lines().map(|lres| {
45        lres.map_err(Error::StdoutUnreadable)
46            .map_err(Into::into)
47            .and_then(|line| {
48                line.parse()
49                    .map_err(Error::ParsingError)
50                    .map_err(Into::into)
51            })
52    }))
53}
54
55/// Executes command with args and environment variables, parses output line by line
56/// * On invalid command: panic
57/// * On error exit code: break iterator
58/// * On parsing failure: panic
59/// * Possible errors: N/A
60///
61/// Designed for
62/// ```rust
63/// use shellfn::shell;
64/// use std::error::Error;
65///
66/// #[shell]
67/// fn command() -> impl Iterator<Item = u32> {
68///     "echo 1; echo 2; echo 3"
69/// }
70///
71/// assert_eq!(vec![1, 2, 3], command().collect::<Vec<_>>())
72/// ```
73pub fn execute_iter_panic_panic<T, TArg, TEnvKey, TEnvVal>(
74    cmd: impl AsRef<OsStr>,
75    args: impl IntoIterator<Item = TArg>,
76    envs: impl IntoIterator<Item = (TEnvKey, TEnvVal)>,
77) -> impl Iterator<Item = T>
78where
79    T: FromStr,
80    TArg: AsRef<OsStr>,
81    TEnvKey: AsRef<OsStr>,
82    TEnvVal: AsRef<OsStr>,
83    <T as FromStr>::Err: StdError,
84{
85    let mut process = spawn(cmd, args, envs).expect(PANIC_MSG);
86    let stdout = process.stdout.take().unwrap();
87
88    BufReader::new(stdout)
89        .lines()
90        .map(|lres| lres.expect(PANIC_MSG).parse().expect(PANIC_MSG))
91        .chain([()].iter().flat_map(move |_| {
92            if !process.wait().unwrap().success() {
93                panic!("{}", PANIC_MSG)
94            }
95            std::iter::empty()
96        }))
97}
98
99/// Executes command with args and environment variables, parses output line by line
100/// * On invalid command: panic
101/// * On error exit code: break iterator
102/// * On parsing failure: yield error item
103/// * Possible errors: StdoutUnreadable (item error), ParsingError (item error)
104///
105/// Designed for
106/// ```rust
107/// use shellfn::shell;
108/// use std::error::Error;
109///
110/// #[shell]
111/// fn command() -> impl Iterator<Item = Result<u32, Box<Error + 'static>>> {
112///     "echo 1; echo 2; echo 3"
113/// }
114///
115/// assert_eq!(vec![1, 2, 3], command().map(Result::unwrap).collect::<Vec<_>>())
116/// ```
117pub fn execute_iter_panic_result<T, TArg, TEnvKey, TEnvVal, TError>(
118    cmd: impl AsRef<OsStr>,
119    args: impl IntoIterator<Item = TArg>,
120    envs: impl IntoIterator<Item = (TEnvKey, TEnvVal)>,
121) -> impl Iterator<Item = Result<T, TError>>
122where
123    T: FromStr,
124    TArg: AsRef<OsStr>,
125    TEnvKey: AsRef<OsStr>,
126    TEnvVal: AsRef<OsStr>,
127    <T as FromStr>::Err: StdError,
128    TError: From<Error<<T as FromStr>::Err>>,
129{
130    let mut process = spawn(cmd, args, envs).expect(PANIC_MSG);
131    let stdout = process.stdout.take().unwrap();
132
133    BufReader::new(stdout)
134        .lines()
135        .map(|lres| {
136            lres.map_err(Error::StdoutUnreadable)
137                .map_err(Into::into)
138                .and_then(|line| {
139                    line.parse()
140                        .map_err(Error::ParsingError)
141                        .map_err(Into::into)
142                })
143        })
144        .chain([()].iter().flat_map(move |_| {
145            if !process.wait().unwrap().success() {
146                panic!("{}", PANIC_MSG)
147            }
148            std::iter::empty()
149        }))
150}
151
152/// Executes command with args and environment variables, parses output line by line
153/// * On invalid command: return empty iterator
154/// * On error exit code: break iterator
155/// * On parsing failure: yield error item
156/// * Possible errors: StdoutUnreadable (item error), ParsingError (item error)
157///
158/// Designed for
159/// ```rust
160/// use shellfn::shell;
161/// use std::error::Error;
162///
163/// #[shell(no_panic)]
164/// fn command() -> impl Iterator<Item = Result<u32, Box<Error + 'static>>> {
165///     "echo 1; echo 2; echo 3"
166/// }
167///
168/// assert_eq!(vec![1, 2, 3], command().map(Result::unwrap).collect::<Vec<_>>())
169/// ```
170pub fn execute_iter_nopanic_result<T, TArg, TEnvKey, TEnvVal, TError>(
171    cmd: impl AsRef<OsStr>,
172    args: impl IntoIterator<Item = TArg>,
173    envs: impl IntoIterator<Item = (TEnvKey, TEnvVal)>,
174) -> impl Iterator<Item = Result<T, TError>>
175where
176    T: FromStr,
177    TArg: AsRef<OsStr>,
178    TEnvKey: AsRef<OsStr>,
179    TEnvVal: AsRef<OsStr>,
180    <T as FromStr>::Err: StdError,
181    TError: From<Error<<T as FromStr>::Err>>,
182{
183    spawn(cmd, args, envs)
184        .ok()
185        .map(|mut process| {
186            BufReader::new(process.stdout.take().unwrap())
187                .lines()
188                .map(|lres| {
189                    lres.map_err(Error::StdoutUnreadable)
190                        .map_err(Into::into)
191                        .and_then(|line| {
192                            line.parse()
193                                .map_err(Error::ParsingError)
194                                .map_err(Into::into)
195                        })
196                })
197        })
198        .map_or_else(|| Either::Right(std::iter::empty()), Either::Left)
199}
200
201/// Executes command with args and environment variables, parses output line by line
202/// * On invalid command: return empty iterator
203/// * On error exit code: break iterator
204/// * On parsing failure: skip item
205/// * Possible errors: N/A
206///
207/// Designed for
208/// ```rust
209/// use shellfn::shell;
210/// use std::error::Error;
211///
212/// #[shell(no_panic)]
213/// fn command() -> impl Iterator<Item = u32> {
214///     "echo 1; echo 2; echo 3"
215/// }
216///
217/// assert_eq!(vec![1, 2, 3], command().collect::<Vec<_>>())
218/// ```
219pub fn execute_iter_nopanic_nopanic<T, TArg, TEnvKey, TEnvVal>(
220    cmd: impl AsRef<OsStr>,
221    args: impl IntoIterator<Item = TArg>,
222    envs: impl IntoIterator<Item = (TEnvKey, TEnvVal)>,
223) -> impl Iterator<Item = T>
224where
225    T: FromStr,
226    TArg: AsRef<OsStr>,
227    TEnvKey: AsRef<OsStr>,
228    TEnvVal: AsRef<OsStr>,
229    <T as FromStr>::Err: StdError,
230{
231    spawn(cmd, args, envs)
232        .ok()
233        .map(|mut process| {
234            BufReader::new(process.stdout.take().unwrap())
235                .lines()
236                .filter_map(|lres| lres.ok().and_then(|line| line.parse().ok()))
237        })
238        .map_or_else(|| Either::Right(std::iter::empty()), Either::Left)
239}
240
241/// Executes command with args and environment variables, parses output line by line
242/// * On invalid command: return error
243/// * On error exit code: break iterator
244/// * On parsing failure: panic
245/// * Possible errors: ProcessNotSpawned
246///
247/// Designed for
248/// ```rust
249/// use shellfn::shell;
250/// use std::error::Error;
251///
252/// #[shell]
253/// fn command() -> Result<impl Iterator<Item = u32>, Box<Error>> {
254///     "echo 1; echo 2; echo 3"
255/// }
256///
257/// assert_eq!(vec![1, 2, 3], command().unwrap().collect::<Vec<_>>())
258/// ```
259pub fn execute_iter_result_panic<T, TArg, TEnvKey, TEnvVal, TError>(
260    cmd: impl AsRef<OsStr>,
261    args: impl IntoIterator<Item = TArg>,
262    envs: impl IntoIterator<Item = (TEnvKey, TEnvVal)>,
263) -> Result<impl Iterator<Item = T>, TError>
264where
265    T: FromStr,
266    TArg: AsRef<OsStr>,
267    TEnvKey: AsRef<OsStr>,
268    TEnvVal: AsRef<OsStr>,
269    <T as FromStr>::Err: StdError,
270    TError: From<Error<<T as FromStr>::Err>>,
271{
272    let mut process = spawn(cmd, args, envs).map_err(Error::ProcessNotSpawned)?;
273    let stdout = process.stdout.take().unwrap();
274
275    Ok(BufReader::new(stdout)
276        .lines()
277        .map(|lres| lres.expect(PANIC_MSG).parse().expect(PANIC_MSG)))
278}
279
280/// Executes command with args and environment variables, parses output line by line
281/// * On invalid command: return error
282/// * On error exit code: break iterator
283/// * On parsing failure: skip item
284/// * Possible errors: ProcessNotSpawned
285///
286/// Designed for
287/// ```rust
288/// use shellfn::shell;
289/// use std::error::Error;
290///
291/// #[shell(no_panic)]
292/// fn command() -> Result<impl Iterator<Item = u32>, Box<Error>> {
293///     "echo 1; echo 2; echo 3"
294/// }
295///
296/// assert_eq!(vec![1, 2, 3], command().unwrap().collect::<Vec<_>>())
297/// ```
298pub fn execute_iter_result_nopanic<T, TArg, TEnvKey, TEnvVal, TError>(
299    cmd: impl AsRef<OsStr>,
300    args: impl IntoIterator<Item = TArg>,
301    envs: impl IntoIterator<Item = (TEnvKey, TEnvVal)>,
302) -> Result<impl Iterator<Item = T>, TError>
303where
304    T: FromStr,
305    TArg: AsRef<OsStr>,
306    TEnvKey: AsRef<OsStr>,
307    TEnvVal: AsRef<OsStr>,
308    <T as FromStr>::Err: StdError,
309    TError: From<Error<<T as FromStr>::Err>>,
310{
311    let mut process = spawn(cmd, args, envs).map_err(Error::ProcessNotSpawned)?;
312    let stdout = process.stdout.take().unwrap();
313
314    Ok(BufReader::new(stdout)
315        .lines()
316        .filter_map(|lres| lres.ok().and_then(|item| item.parse().ok())))
317}