use crate::models::{CrateId, DepGraph};
mod executor;
#[derive(Debug)]
pub enum Query {
Set(Vec<String>),
Path(String),
Regex(String),
DepGraph(Vec<DepConstraint>),
}
impl Query {
pub fn parse<'a>(line: impl Iterator<Item = String>) -> (Self, Vec<String>) {
let parser = QueryParser::new(line.skip(1).collect());
match parser.run() {
Some((q, rest)) => (q, rest),
None => {
eprintln!("Failed to parse query!");
std::process::exit(2);
}
}
}
pub fn execute(self, g: &DepGraph) -> Vec<CrateId> {
match self {
Self::Set(ref crates) => executor::set(crates, g),
Self::DepGraph(deps) => executor::deps(deps, g),
_ => todo!(),
}
}
}
#[derive(Debug)]
pub struct DepConstraint {
pub _crate: String,
pub constraint: Constraint,
}
#[derive(Debug)]
pub enum Constraint {
Initial(bool),
And(bool),
Or,
}
struct QueryParser {
line: Vec<String>,
}
impl QueryParser {
fn new(line: Vec<String>) -> Self {
Self { line }
}
fn run(mut self) -> Option<(Query, Vec<String>)> {
let line: Vec<String> =
std::mem::replace(&mut self.line, vec![])
.into_iter()
.fold(vec![], |mut vec, line| {
line.split(" ").for_each(|seg| {
vec.push(seg.into());
});
vec
});
#[derive(Debug)]
enum Brace {
Block,
Curly,
}
#[derive(Debug)]
enum BraceState {
Missing,
BlockOpen,
CurlyOpen,
Done(Brace),
}
use {Brace::*, BraceState::*};
let mut bs = Missing;
let mut buf = vec![];
let mut cbuf = String::new(); let mut skip = 1;
for elem in &line {
match (&bs, elem.as_str()) {
(Missing, e) if e.starts_with("[") => {
bs = BlockOpen;
if let Some(_crate) = e.strip_prefix("[") {
if _crate != "" {
buf.push(_crate.to_string());
}
}
}
(Missing, e) if e.starts_with("{") => {
bs = CurlyOpen;
if let Some(_crate) = e.strip_prefix("{") {
if _crate != "" {
cbuf = _crate.into();
}
}
}
(BlockOpen, e) if e.ends_with("]") => {
if let Some(_crate) = e.strip_suffix("]") {
if _crate != "" {
buf.push(_crate.to_string());
}
}
bs = Done(Block);
break;
}
(BlockOpen, _crate) => buf.push(_crate.to_string()),
(CurlyOpen, e) if e.ends_with("}") && cbuf == "" => {
bs = Done(Curly);
break;
}
(CurlyOpen, e) if e.ends_with("}") && cbuf != "" => {
eprintln!("[ERROR]: Out of place `}}`, expected operand!");
std::process::exit(2);
}
(CurlyOpen, op) if cbuf != "" => {
buf.push(format!("{} $ {}", cbuf, op));
cbuf = "".into();
}
(CurlyOpen, _crate) => {
cbuf = _crate.into();
}
(_, _) => {}
}
skip += 1;
}
let rest = line.into_iter().skip(skip).collect();
match bs {
Done(Block) => Some((Query::Set(buf), rest)),
Done(Curly) => {
let mut init = true;
let c: Vec<_> = buf
.into_iter()
.map(|val| {
let mut s: Vec<_> = val.split("$").collect();
let _crate = s.remove(0).trim().to_string();
let c = s.remove(0).trim().to_string();
DepConstraint {
_crate,
constraint: match c.as_str() {
"<" if init => {
init = false;
Constraint::Initial(true)
}
"!<" if init => {
init = false;
Constraint::Initial(false)
}
"&<" => Constraint::And(true),
"!&<" => Constraint::And(false),
"|<" => Constraint::Or,
c => {
eprintln!("[ERROR]: Invalid constraint: `{}`", c);
std::process::exit(2);
}
},
}
})
.collect();
if c.len() < 1 {
eprintln!("[ERROR]: Provided an empty graph set: {{ }}. At least one dependency required!");
std::process::exit(2);
}
Some((Query::DepGraph(c), rest))
}
_ if rest.len() < 1 => crate::cli::render_help(2),
_line => {
eprintln!("[ERROR]: You reached some unimplemented code in cargo-ws2! \
This might be a bug, or it might be a missing feature. Contact me with your query, \
and we can see which one it is :)");
std::process::exit(2);
}
}
}
}
#[test]
fn block_parser_spaced() {
let _ = QueryParser::new(
vec!["", "[", "foo", "bar", "baz", "]", "publish", "minor"]
.into_iter()
.map(Into::into)
.collect(),
)
.run();
}
#[test]
fn block_parser_offset_front() {
let _ = QueryParser::new(
vec!["my-program", "[foo", "bar", "baz", "]", "publish", "minor"]
.into_iter()
.map(Into::into)
.collect(),
)
.run();
}
#[test]
fn block_parser_offset_back() {
let _ = QueryParser::new(
vec!["my-program", "[", "foo", "bar", "baz]", "publish", "minor"]
.into_iter()
.map(Into::into)
.collect(),
)
.run();
}
#[test]
fn block_parser_offset_both() {
let _ = QueryParser::new(
vec!["my-program", "[foo", "bar", "baz]", "publish", "minor"]
.into_iter()
.map(Into::into)
.collect(),
)
.run();
}
#[test]
fn curly_parser_simple() {
let _ = QueryParser::new(
vec!["my-program", "{ foo < bar &< }", "print"]
.into_iter()
.map(Into::into)
.collect(),
)
.run();
}