1use std::{convert::TryFrom, fmt, io, str::FromStr};
2
3use crate::{
4 builder::{Builder, Config},
5 error::{access::AccessError, input::InputError},
6 parsers::InputType,
7};
8
9#[derive(Debug)]
11pub struct Input {
12 kind: InputType,
13}
14
15impl Input {
16 pub fn builder() -> Builder {
21 Builder::default()
22 }
23
24 pub fn with_defaults(input: impl AsRef<str>) -> Result<Self, InputError> {
26 Config::default().parse(input.as_ref())
27 }
28
29 pub fn access(&self) -> Result<InputReader, AccessError> {
32 Read::try_from(&self.kind).map(InputReader::new)
33 }
34
35 pub(crate) fn from_input_type(i: InputType) -> Self {
36 Self { kind: i }
37 }
38}
39
40impl FromStr for Input {
41 type Err = InputError;
42
43 fn from_str(s: &str) -> Result<Self, Self::Err> {
44 Self::with_defaults(s)
45 }
46}
47
48#[derive(Debug)]
50pub struct InputReader {
51 input: Read,
52}
53
54impl InputReader {
55 fn new(input: Read) -> Self {
56 Self { input }
57 }
58
59 pub fn read_to_string(&mut self) -> Result<String, io::Error> {
63 let mut buf = String::new();
64
65 io::Read::read_to_string(&mut self.input, &mut buf)?;
66
67 Ok(buf)
68 }
69}
70
71impl io::Read for InputReader {
72 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
73 io::Read::read(&mut self.input, buf)
74 }
75}
76
77enum Read {
78 File(std::fs::File),
79 Stdin(std::io::Stdin),
80 Text(io::Cursor<String>),
81}
82
83impl Read {
84 fn stdin() -> Self {
85 Self::Stdin(io::stdin())
86 }
87
88 fn file(f: std::fs::File) -> Self {
89 Self::File(f)
90 }
91
92 fn text(s: impl AsRef<str>) -> Self {
93 let s = s.as_ref().to_string();
94
95 Self::Text(io::Cursor::new(s))
96 }
97}
98
99impl TryFrom<&InputType> for Read {
100 type Error = AccessError;
101
102 fn try_from(kind: &InputType) -> Result<Self, Self::Error> {
103 match kind {
104 InputType::Stdin => Ok(Read::stdin()),
105 InputType::File(ref f) => std::fs::File::open(f.path.as_path())
106 .map(Read::file)
107 .map_err(|e| AccessError::file_with_context(e, f.path.as_path())),
108 InputType::UTF8(ref s) => Ok(Self::text(s)),
109 }
110 }
111}
112
113impl io::Read for Read {
114 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
115 use Read::*;
116 match self {
117 File(ref mut file) => io::Read::read(file, buf),
118 Stdin(ref mut stdin) => io::Read::read(stdin, buf),
119 Text(ref mut cursor) => io::Read::read(cursor, buf),
120 }
121 }
122}
123
124impl fmt::Debug for Read {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 use Read::*;
127 let mut dbg = f.debug_struct("Read");
128
129 match self {
130 File(f) => dbg.field("file", &f),
131 Stdin(s) => dbg.field("stdin", &s),
132 Text(t) => dbg.field("cursor", &t),
133 };
134
135 dbg.finish()
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn input_from_default() {
145 let input = "@/some/file/path";
146 let res = Input::with_defaults(input);
147
148 assert!(res.is_ok())
149 }
150
151 #[test]
152 fn input_from_str() {
153 let input = "some text";
154 let res = Input::from_str(input);
155
156 assert!(res.is_ok())
157 }
158
159 #[test]
160 fn input_reader() {
161 let input = "some random text";
162 let i = Input::with_defaults(input).unwrap();
163
164 let output = i.access().unwrap().read_to_string().unwrap();
165
166 assert_eq!(input, output.as_str())
167 }
168}