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 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
// FETCHER.rs
// by Lut99
//
// Created:
// 14 Sep 2022, 11:32:04
// Last edited:
// 01 Mar 2023, 09:51:43
// Auto updated?
// Yes
//
// Description:
//! Implements the SnippetFetcher, which will attempt to get multiple
//! lines until a parseable snippet has been reached.
//
use std::error::Error;
use brane_dsl::TextPos;
use log::error;
/***** LIBRARY *****/
/// Iterator that will progressively get new lines until a parseable snippet has been reached.
pub struct SnippetFetcher<'a> {
/// A closure we use to get new lines.
#[allow(clippy::type_complexity)]
pub get_line: Box<dyn 'a + FnMut() -> Result<Option<String>, Box<dyn Error>>>,
}
impl<'a> SnippetFetcher<'a> {
/// Constructor for the SnippetFetcher that will fetch using the given closure.
///
/// # Generic arguments
/// - `F`: The type of the given fetcher closure. This closure should return line-by-line, or `None` if a (permanent) end-of-file is reached. Note that it is assumed that the returned line does _not_ have a newline appended to it at the end. Optionally, it may also return an Error of any kind.
///
/// # Arguments
/// - `fetcher`: The function to fetch new lines with.
///
/// # Returns
/// A new SnippetFetcher iterator instance.
#[inline]
pub fn new<F>(fetcher: F) -> Self
where
F: 'a + FnMut() -> Result<Option<String>, Box<dyn Error>>,
{
Self { get_line: Box::new(fetcher) }
}
}
impl<'a> Iterator for SnippetFetcher<'a> {
type Item = (TextPos, String);
fn next(&mut self) -> Option<Self::Item> {
// Get snippets until enough
let mut offset: TextPos = TextPos::new(0, 0);
let mut buffer: String = String::new();
let mut n_open_paren: usize = 0;
let mut n_open_square: usize = 0;
let mut n_open_curly: usize = 0;
let mut n_open_triang: usize = 0;
loop {
// Get the next line
let line: String = match (self.get_line)() {
Ok(res) => match res {
Some(line) => line,
None => {
return None;
},
},
Err(err) => {
error!("Failed to fetch new line: {}", err);
return None;
},
};
offset.line += 1;
// Analyze if any BraneScript thingamabobs are opened (i.e., brackets of any kind)
for c in line.chars() {
match c {
// Open
'(' => {
n_open_paren += 1;
},
'[' => {
n_open_square += 1;
},
'{' => {
n_open_curly += 1;
},
'<' => {
n_open_triang += 1;
},
// Close
')' => {
n_open_paren = n_open_paren.saturating_sub(1);
},
']' => {
n_open_square = n_open_square.saturating_sub(1);
},
'}' => {
n_open_curly = n_open_curly.saturating_sub(1);
},
'>' => {
n_open_triang = n_open_triang.saturating_sub(1);
},
// The rest is fine
_ => {},
}
offset.col += 1;
}
// If there any are opened, try again
buffer.push_str(&line);
buffer.push('\n');
if n_open_paren > 0 || n_open_square > 0 || n_open_curly > 0 || n_open_triang > 0 {
continue;
}
return Some((offset, buffer));
}
}
}