feat(users/Profpatsch/netencode.rs): parse multiple stdin values
Adds support for parsing multiple netencode values from stdin. This is overly complicated for my tastes, but I don’t see a better way of writing this logic that does not read all of stdin before starting to parse the first value. A kingdom for a conduit. Change-Id: Ia4f849d4096c43e887756b756d2a85d7f9cd380a Reviewed-on: https://cl.tvl.fyi/c/depot/+/6631 Autosubmit: Profpatsch <mail@profpatsch.de> Reviewed-by: Profpatsch <mail@profpatsch.de> Tested-by: BuildkiteCI
This commit is contained in:
		
							parent
							
								
									d44203d046
								
							
						
					
					
						commit
						0f74816d43
					
				
					 4 changed files with 104 additions and 31 deletions
				
			
		|  | @ -198,25 +198,103 @@ pub fn text(s: String) -> T { | |||
|     T::Text(s) | ||||
| } | ||||
| 
 | ||||
| pub fn u_from_stdin_or_die_user_error<'a>(prog_name: &'_ str, stdin_buf: &'a mut Vec<u8>) -> U<'a> { | ||||
|     std::io::stdin().lock().read_to_end(stdin_buf); | ||||
|     let u = match parse::u_u(stdin_buf) { | ||||
|         Ok((rest, u)) => match rest { | ||||
|             b"" => u, | ||||
|             _ => exec_helpers::die_user_error( | ||||
| pub fn t_from_stdin_or_die_user_error<'a>(prog_name: &'_ str) -> T { | ||||
|     match t_from_stdin_or_die_user_error_with_rest(prog_name, &vec![]) { | ||||
|         None => exec_helpers::die_user_error(prog_name, "stdin was empty"), | ||||
|         Some((rest, t)) => { | ||||
|             if rest.is_empty() { | ||||
|                 t | ||||
|             } else { | ||||
|                 exec_helpers::die_user_error( | ||||
|                     prog_name, | ||||
|                     format!( | ||||
|                         "stdin contained some soup after netencode value: {:?}", | ||||
|                         String::from_utf8_lossy(&rest) | ||||
|                     ), | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Read a netencode value from stdin incrementally, return bytes that could not be read.
 | ||||
| /// Nothing if there was nothing to read from stdin & no initial_bytes were provided.
 | ||||
| /// These can be passed back as `initial_bytes` if more values should be read.
 | ||||
| pub fn t_from_stdin_or_die_user_error_with_rest<'a>( | ||||
|     prog_name: &'_ str, | ||||
|     initial_bytes: &[u8], | ||||
| ) -> Option<(Vec<u8>, T)> { | ||||
|     let mut chonker = Chunkyboi::new(std::io::stdin().lock(), 4096); | ||||
|     // The vec to pass to the parser on each step
 | ||||
|     let mut parser_vec: Vec<u8> = initial_bytes.to_vec(); | ||||
|     // whether stdin was already empty
 | ||||
|     let mut was_empty: bool = false; | ||||
|     loop { | ||||
|         match chonker.next() { | ||||
|             None => { | ||||
|                 if parser_vec.is_empty() { | ||||
|                     return None; | ||||
|                 } else { | ||||
|                     was_empty = true | ||||
|                 } | ||||
|             } | ||||
|             Some(Err(err)) => exec_helpers::die_temporary( | ||||
|                 prog_name, | ||||
|                 format!( | ||||
|                     "stdin contained some soup after netencode value: {:?}", | ||||
|                     String::from_utf8_lossy(rest) | ||||
|                 ), | ||||
|                 &format!("could not read from stdin: {:?}", err), | ||||
|             ), | ||||
|         }, | ||||
|         Err(err) => exec_helpers::die_user_error( | ||||
|             prog_name, | ||||
|             format!("unable to parse netencode from stdin: {:?}", err), | ||||
|         ), | ||||
|     }; | ||||
|     u | ||||
|             Some(Ok(mut new_bytes)) => parser_vec.append(&mut new_bytes), | ||||
|         } | ||||
| 
 | ||||
|         match parse::t_t(&parser_vec) { | ||||
|             Ok((rest, t)) => return Some((rest.to_owned(), t)), | ||||
|             Err(nom::Err::Incomplete(Needed)) => { | ||||
|                 if was_empty { | ||||
|                     exec_helpers::die_user_error( | ||||
|                         prog_name, | ||||
|                         &format!( | ||||
|                             "unable to parse netencode from stdin, input incomplete: {:?}", | ||||
|                             parser_vec | ||||
|                         ), | ||||
|                     ); | ||||
|                 } | ||||
|                 // read more from stdin and try parsing again
 | ||||
|                 continue; | ||||
|             } | ||||
|             Err(err) => exec_helpers::die_user_error( | ||||
|                 prog_name, | ||||
|                 &format!("unable to parse netencode from stdin: {:?}", err), | ||||
|             ), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // iter helper
 | ||||
| // TODO: put into its own module
 | ||||
| struct Chunkyboi<T> { | ||||
|     inner: T, | ||||
|     buf: Vec<u8>, | ||||
| } | ||||
| 
 | ||||
| impl<R: Read> Chunkyboi<R> { | ||||
|     fn new(inner: R, chunksize: usize) -> Self { | ||||
|         let buf = vec![0; chunksize]; | ||||
|         Chunkyboi { inner, buf } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<R: Read> Iterator for Chunkyboi<R> { | ||||
|     type Item = std::io::Result<Vec<u8>>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> { | ||||
|         match self.inner.read(&mut self.buf) { | ||||
|             Ok(0) => None, | ||||
|             Ok(read) => { | ||||
|                 // clone a new buffer so we can reuse the internal one
 | ||||
|                 Some(Ok(self.buf[..read].to_owned())) | ||||
|             } | ||||
|             Err(err) => Some(Err(err)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub mod parse { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue