1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//! Provides the higher order `IO` function [`interact`](crate::interact) for prototyping command line interfaces.
//!
//! *A small tutorial package for Haskell refugees adapted from this lovely [walkthrough](https://wiki.haskell.org/Tutorials/Programming_Haskell/String_IO#IO).*
//!
//! Interact takes a function of type `Fn(String) -> String`, runs it on `stdin`
//! and prints the result to `stdout`.
//!
//! # Examples
//! ## `cat`
//! ```
//! use interakt::interact;
//! use std::convert::identity;
//!
//! fn main() -> std::io::Result<()> { interact(identity) }
//! ```
//! ```shell
//! $ cat cat.rs | cargo run --bin cat
//! use interakt::interact;
//! use std::convert::identity;
//!
//! fn main() -> std::io::Result<()> { interact(identity) }
//! ```
//! ## `wc`
//! ```
//! # use interakt::interact;
//! fn main() -> std::io::Result<()> {
//!     let count = |s: String| -> String { format!("{}\n", s.len()) };
//!     interact(count)
//! }
//! ```
//! ```shell
//! $ cat cat.rs | cargo run --bin wc
//! 109
//! ```
//! ## `wc -l`
//! ```
//! # use interakt::interact;
//! fn main() -> std::io::Result<()> {
//!     let count_lines = |s: String| -> String {
//!         format!("{}\n", s.lines().collect::<Vec<_>>().len())
//!     };
//!     interact(count_lines)
//! }
//! ```
//! ```shell
//! $ cat cat.rs | cargo run --bin wcl
//! 4
//! ```
//! ## `rev`
//! ```
//! # use interakt::interact;
//! fn main() -> std::io::Result<()> {
//!     let rev_lines = |s: String| -> String {
//!         s.lines()
//!          .map(|line| line.chars().rev().collect())
//!          .collect::<Vec<String>>()
//!          .join("\n")
//!     };
//!     interact(rev_lines)
//! }
//! ```
//! ```text
//! $ cat cat.rs | cargo run --bin rev
//! ;tcaretni::tkaretni esu
//! ;ytitnedi::trevnoc::dts esu
//!
//! } )ytitnedi(tcaretni { >)(<tluseR::oi::dts >- )(niam nf
//! ```
use std::io::{Read, Write};

/// The higher-order `interact` function.
///
/// In Haskell, the interact function is:
/// ```text
/// interact f = do s <- getContents
///                 putStr (f s)
/// ```
pub fn interact<F>(f: F) -> std::io::Result<()>
    where F: std::ops::Fn(String) -> String
{
    let mut buffer = String::new();
    std::io::stdin().read_to_string(&mut buffer)?;
    std::io::stdout().write_all(f(buffer).as_bytes())?;
    Ok(())
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}