analyse_json/json/ndjson/errors/
collection.rs1use std::cell::RefCell;
2use std::error::Error;
3use std::fmt::Display;
4use std::iter::Enumerate;
5use std::rc::Rc;
6use std::sync::{Arc, Mutex};
7use std::{fmt, io};
8
9use owo_colors::{OwoColorize, Stream};
10
11use super::NDJSONError;
12
13#[derive(Debug)]
15pub struct IndexedNDJSONError {
16 pub location: String,
17 pub error: NDJSONError,
18}
19
20impl IndexedNDJSONError {
21 pub(crate) fn new(location: String, error: NDJSONError) -> Self {
22 Self { location, error }
23 }
24}
25
26impl fmt::Display for IndexedNDJSONError {
27 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28 write!(f, "Line {}: {}", self.location, self.error)?;
29 if let Some(source) = self.error.source() {
30 write!(f, "; {source}")?;
31 }
32 Ok(())
33 }
34}
35
36#[derive(Debug)]
39pub struct ErrorsPar<E> {
40 pub container: Arc<Mutex<Vec<E>>>,
41}
42
43impl<E> ErrorsPar<E> {
44 pub fn new(container: Arc<Mutex<Vec<E>>>) -> Self {
45 Self { container }
46 }
47
48 pub fn new_ref(&self) -> Self {
49 Self {
50 container: Arc::clone(&self.container),
51 }
52 }
53
54 pub fn push(&self, value: E) {
55 self.container.lock().expect("not poisoned").push(value)
56 }
57}
58
59impl<E> Default for ErrorsPar<E> {
60 fn default() -> Self {
61 Self::new(Arc::new(Mutex::new(vec![])))
62 }
63}
64
65impl<E: Display> fmt::Display for ErrorsPar<E> {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 for i in self.container.lock().unwrap().as_slice() {
68 writeln!(f, "{i}")?;
69 }
70 Ok(())
71 }
72}
73
74pub trait NDJSONProcessingErrors {
76 fn eprint(&self) {}
77}
78
79impl<E: Display> NDJSONProcessingErrors for ErrorsPar<E> {
80 fn eprint(&self) {
81 let stream = Stream::Stdout;
82 if !self.container.lock().unwrap().is_empty() {
83 eprintln!("{}", self.if_supports_color(stream, |text| text.red()));
84 }
85 }
86}
87
88#[derive(Debug)]
92pub struct Errors<E> {
93 pub container: Rc<RefCell<Vec<E>>>,
94}
95
96impl<E> Errors<E> {
97 pub fn new(container: Rc<RefCell<Vec<E>>>) -> Self {
98 Self { container }
99 }
100
101 pub fn new_ref(&self) -> Self {
102 Self {
103 container: Rc::clone(&self.container),
104 }
105 }
106
107 pub fn push(&self, value: E) {
108 self.container.borrow_mut().push(value)
109 }
110}
111
112impl<E: Display> NDJSONProcessingErrors for Errors<E> {
113 fn eprint(&self) {
114 let stream = Stream::Stdout;
115 if !self.container.borrow().is_empty() {
116 eprintln!("{}", self.if_supports_color(stream, |text| text.red()));
117 }
118 }
119}
120
121impl<E> Default for Errors<E> {
122 fn default() -> Self {
123 Self::new(Rc::new(RefCell::new(vec![])))
124 }
125}
126
127impl<E: Display> fmt::Display for Errors<E> {
128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129 for i in self.container.borrow().as_slice() {
130 writeln!(f, "{i}")?;
131 }
132 Ok(())
133 }
134}
135
136pub struct ErrFiltered<I, E> {
138 iter: I,
139 errors: Errors<E>,
140}
141
142impl<U, E, T, I: Iterator<Item = (U, Result<T, W>)>, W> ErrFiltered<I, E> {
143 pub fn new(iter: I, errors: Errors<E>) -> Self {
144 Self { iter, errors }
145 }
146}
147
148impl<U: Display, T, I: Iterator<Item = (U, Result<T, impl Into<NDJSONError>>)>> Iterator
149 for ErrFiltered<I, IndexedNDJSONError>
150{
151 type Item = (U, T);
152 fn next(&mut self) -> Option<Self::Item> {
153 loop {
154 let (id, next_item) = self.iter.next()?;
155 match next_item {
156 Ok(item) => break Some((id, item)),
157 Err(e) => {
158 let error: NDJSONError = e.into();
159 self.errors
160 .push(IndexedNDJSONError::new(format!("{id}"), error));
161 }
162 }
163 }
164 }
165}
166
167pub trait IntoErrFiltered<U, E, T, W>: Iterator<Item = (U, Result<T, W>)> + Sized {
168 fn to_err_filtered(self, errors: Errors<E>) -> ErrFiltered<Self, E> {
169 ErrFiltered::new(self, errors)
170 }
171}
172
173impl<U, E, T, I: Iterator<Item = (U, Result<T, W>)>, W> IntoErrFiltered<U, E, T, W> for I {}
174
175pub struct EnumeratedErrFiltered<I, E> {
177 iter: Enumerate<I>,
178 errors: Errors<E>,
179}
180
181impl<E, T, I: Iterator<Item = Result<T, W>>, W> EnumeratedErrFiltered<I, E> {
182 pub fn new(iter: I, errors: Errors<E>) -> Self {
183 Self {
184 iter: iter.enumerate(),
185 errors,
186 }
187 }
188}
189
190impl<T, I> Iterator for EnumeratedErrFiltered<I, IndexedNDJSONError>
191where
192 I: Iterator<Item = Result<T, io::Error>>,
193{
194 type Item = (usize, T);
195 fn next(&mut self) -> Option<Self::Item> {
196 loop {
197 let (i, next_item) = self.iter.next()?;
198 let i = i + 1; match next_item {
200 Ok(item) => break Some((i, item)),
201 Err(e) => {
202 self.errors.push(IndexedNDJSONError::new(
203 i.to_string(),
204 NDJSONError::IOError(e),
205 ));
206 }
207 }
208 }
209 }
210}
211
212pub trait IntoEnumeratedErrFiltered<E, T, W>: Iterator<Item = Result<T, W>> + Sized {
213 fn to_enumerated_err_filtered(self, errors: Errors<E>) -> EnumeratedErrFiltered<Self, E> {
214 EnumeratedErrFiltered::new(self, errors)
215 }
216}
217
218impl<E, T, I: Iterator<Item = Result<T, W>>, W> IntoEnumeratedErrFiltered<E, T, W> for I {}