use crate::{
engine::{error::EngineError, Input},
input::{error::InputErrorConvertible as _, InputBlockIterator as _},
is_json_whitespace,
result::{empty::EmptyRecorder, Match, MatchCount, MatchIndex, MatchSpan, Sink},
BLOCK_SIZE,
};
pub(super) fn count<I>(input: &I) -> Result<MatchCount, EngineError>
where
I: Input,
{
if input.seek_non_whitespace_forward(0).e()?.is_some() {
Ok(1)
} else {
Ok(0)
}
}
pub(super) fn index<I, S>(input: &I, sink: &mut S) -> Result<(), EngineError>
where
I: Input,
S: Sink<MatchIndex>,
{
if let Some((first_idx, _)) = input.seek_non_whitespace_forward(0).e()? {
sink.add_match(first_idx - input.leading_padding_len())
.map_err(|err| EngineError::SinkError(Box::new(err)))?;
}
Ok(())
}
pub(super) fn approx_span<I, S>(input: &I, sink: &mut S) -> Result<(), EngineError>
where
I: Input,
S: Sink<MatchSpan>,
{
if let Some((first_idx, _)) = input.seek_non_whitespace_forward(0).e()? {
let end_idx = match input.len_hint() {
Some(end_idx) => end_idx, None => {
let mut iter = input.iter_blocks::<_, BLOCK_SIZE>(&EmptyRecorder);
let mut end_idx = 0;
while (iter.next().e()?).is_some() {
end_idx += BLOCK_SIZE;
}
end_idx
}
};
sink.add_match(MatchSpan::from_indices(
first_idx - input.leading_padding_len(),
end_idx - input.leading_padding_len(),
))
.map_err(|err| EngineError::SinkError(Box::new(err)))?;
}
Ok(())
}
pub(super) fn match_<I, S>(input: &I, sink: &mut S) -> Result<(), EngineError>
where
I: Input,
S: Sink<Match>,
{
let mut iter = input.iter_blocks::<_, BLOCK_SIZE>(&EmptyRecorder);
let mut res: Vec<u8> = vec![];
let mut first_significant_idx = None;
let mut offset = 0;
while let Some(block) = iter.next().e()? {
if first_significant_idx.is_none() {
first_significant_idx = block.iter().position(|&x| !is_json_whitespace(x));
if let Some(first_idx) = first_significant_idx {
res.extend(&block[first_idx..]);
} else {
offset += block.len();
}
} else {
res.extend(&*block);
}
}
if let Some(start) = first_significant_idx {
while !res.is_empty() && is_json_whitespace(res[res.len() - 1]) {
res.pop();
}
let actual_start = start + offset - input.leading_padding_len();
sink.add_match(Match::from_start_and_bytes(actual_start, res))
.map_err(|err| EngineError::SinkError(Box::new(err)))?;
}
Ok(())
}