findfile 0.2.8

An ergonomic way to search for files
Documentation
use crate::ast::Block;
use crate::parse::{LexContext, ParseError, Token};
use os_str_bytes::OsStrBytes;

#[derive(Debug, Clone, PartialEq)]
pub struct Interpolated {
	pub parts: Vec<(Vec<u8>, Block)>,
	pub tail: Vec<u8>,
}

pub trait End {
	fn matches(&self, token: &Token) -> bool;
}

impl End for Token {
	fn matches(&self, token: &Token) -> bool {
		*self == *token
	}
}

impl<F: Fn(&Token) -> bool> End for F {
	fn matches(&self, token: &Token) -> bool {
		self(token)
	}
}

impl Interpolated {
	pub fn parse_until(lctx: &mut LexContext, end: impl End) -> Result<(Self, Token), ParseError> {
		let mut parts = Vec::new();
		let mut current = Vec::new();

		loop {
			match lctx.next()?.expect("this should be an error in the lexer") {
				Token::CliArg(pos) => {
					let cli = lctx.get_cli(pos).ok_or(ParseError::InvalidCliPosition(pos))?;
					current.extend(cli.to_raw_bytes().iter());
				}
				Token::EnvVar(var) => {
					let env = lctx.get_env(&var).ok_or(ParseError::MissingEnvVar(var))?;
					current.extend(env.to_raw_bytes().iter());
				}
				Token::Raw(data) => current.extend(&data),
				Token::BeginBraceEscape => {
					let expr = Block::parse_until(lctx, Token::EndBraceEscape)?;
					parts.push((std::mem::take(&mut current), expr));
				}
				token if end.matches(&token) => return Ok((Self { parts, tail: current }, token)),
				token => unreachable!("invalid token in interpolation: {token:?}"),
			}
		}
	}
}