From 1762bebef9fedd0ae7fc44833a2a6dd711c716ed Mon Sep 17 00:00:00 2001 From: Emanuel Dima Date: Wed, 20 Mar 2024 17:10:46 +0100 Subject: [PATCH] support assignment, fix path stack overflow --- README.md | 9 +++++ doc/issues.md | 3 +- src/api/error.rs | 25 ++++++++---- src/api/internal/elevation.rs | 5 ++- src/api/value.rs | 9 +++++ src/api/xell.rs | 52 ++++++++++++++++--------- src/interpretations/http.rs | 2 - src/prog/mod.rs | 4 +- src/prog/parse_path.rs | 24 ++++-------- src/prog/parse_program.rs | 26 ++++++++++--- src/prog/path.rs | 2 + src/prog/program.rs | 37 ++++++++++-------- src/prog/searcher.rs | 2 +- src/tests/data/assignment.json | 3 ++ {examples => src/tests/data}/write.json | 0 {examples => src/tests/data}/write.txt | 0 {examples => src/tests/data}/write2.txt | 0 {examples => src/tests/data}/write3.txt | 0 src/tests/fs.rs | 27 ++++++++++--- src/tests/ideals.rs | 52 ------------------------- src/tests/mod.rs | 2 +- src/tests/program.rs | 47 ++++++++++++++++++++++ src/tests/search.rs | 23 ++--------- src/utils/log.rs | 2 +- 24 files changed, 204 insertions(+), 152 deletions(-) create mode 100644 src/tests/data/assignment.json rename {examples => src/tests/data}/write.json (100%) rename {examples => src/tests/data}/write.txt (100%) rename {examples => src/tests/data}/write2.txt (100%) rename {examples => src/tests/data}/write3.txt (100%) delete mode 100644 src/tests/ideals.rs create mode 100644 src/tests/program.rs diff --git a/README.md b/README.md index 2e8dc25..661d4fb 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,15 @@ hial 'diff ./file.json^json^tree ./file.xml^xml^tree' # 🚧 todo: support tree implementation and conversion ``` +Diff the files listed in a rust `mod` file against the actual list of files + +```bash +hial 'diff + ./src/tests/mod.rs^rust/**/*[:mod_item]/name#value + ./src/tests/*[:file]#label^regex["([^.]+).*"]/*/[0]' +# 🚧 todo: support diff +``` + Diff two diff trees (e.g. check if two different commits make identical changes) ```bash diff --git a/doc/issues.md b/doc/issues.md index 3508285..c06d332 100644 --- a/doc/issues.md +++ b/doc/issues.md @@ -1,8 +1,7 @@ # List of Todos and other Issues -- set value on the command line: '/username = "newuser"' - add split(":") interpretation, read-write -- '**[filter]' must be work as '**/*[filter]' (filter to be applied only on leaves) +- '**[filter]' must work as '**/*[filter]' (filter to be applied only on leaves) - support rust/ts write: `hial './src/tests/rust.rs^rust/*[:function_item].label = "modified_fn_name"'` - add interpretation params to Xell::be() - support zip, markdown diff --git a/src/api/error.rs b/src/api/error.rs index 5319666..650a79c 100644 --- a/src/api/error.rs +++ b/src/api/error.rs @@ -59,11 +59,13 @@ impl HErr { pub(crate) fn with_path(self, path: impl Into) -> Self { let path = path.into(); if let Err(old_path) = self.data.cell_path.set(path.clone()) { - warning!( - "overwrote cell path to augment HErr: {} -> {}", - old_path, - path - ); + if old_path != path && !old_path.is_empty() { + warning!( + "overwrote cell path to augment HErr: {} -> {}", + old_path, + path + ); + } } self } @@ -95,22 +97,29 @@ impl ResHErrAugmentation for Res { } } -pub fn noerr() -> HErr { +pub fn noerrm(message: impl Into) -> HErr { HErr { kind: HErrKind::None, data: Rc::new(HErrData { - msg: String::new(), + msg: message.into(), cell_path: OnceCell::new(), cause: None, backtrace: Some(capture_stack_trace()), }), } } +pub fn noerr() -> HErr { + noerrm(String::new()) +} pub fn nores() -> Res { Err(noerr()) } +pub fn noresm(message: impl Into) -> Res { + Err(noerrm(message)) +} + pub fn usererr(reason: impl Into) -> HErr { HErr { kind: HErrKind::User, @@ -196,7 +205,7 @@ impl std::fmt::Display for HErr { } if let Some(path) = self.data.cell_path.get() { - write!(f, " -- at cell path: {}", path)?; + write!(f, " -- cell path: {}", path)?; } if let Some(ref cause) = self.data.cause { write!(f, " -- caused by: {}", cause)?; diff --git a/src/api/internal/elevation.rs b/src/api/internal/elevation.rs index e71129d..ebfc0c5 100644 --- a/src/api/internal/elevation.rs +++ b/src/api/internal/elevation.rs @@ -364,7 +364,10 @@ impl Group { match self.kind { GroupKind::Root => { let Some(entry) = data.map.get_full(label.as_cow_str().as_ref()) else { - return nores(); + return noresm(format!( + "no such interpretation `{}`", + label.as_cow_str().as_ref() + )); }; let cell = Cell { data: self.data.clone(), diff --git a/src/api/value.rs b/src/api/value.rs index 8a4cb7c..03a6293 100644 --- a/src/api/value.rs +++ b/src/api/value.rs @@ -247,6 +247,15 @@ where } } +impl PartialEq for Value<'_> { + fn eq(&self, other: &Int) -> bool { + match self { + Value::Int(i) => i.eq(other), + _ => false, + } + } +} + /////////////////////////////////////////////////////////////////////////////// // OwnValue diff --git a/src/api/xell.rs b/src/api/xell.rs index aa2fe30..566d49c 100644 --- a/src/api/xell.rs +++ b/src/api/xell.rs @@ -303,22 +303,22 @@ impl Xell { let (start, path) = Path::parse_with_starter(path_with_start)?; let root = start.eval()?; let mut searcher = Searcher::new(root, path); + let path = format!("{}{}", start, searcher.unmatched_path().as_str()); match searcher.next() { Some(Ok(c)) => Ok(c), - Some(Err(e)) => Err(e), - None => nores(), + Some(Err(e)) => Err(e.with_path(path)), + None => nores().with_path(path), } } pub(crate) fn new_from(dyn_cell: DynCell, origin: Option) -> Xell { + let wp = origin + .as_ref() + .map_or(WritePolicy::ReadOnly, |c| c.domain.write_policy.get()); Xell { dyn_cell, domain: Rc::new(Domain { - write_policy: cell::Cell::new( - origin - .as_ref() - .map_or(WritePolicy::ReadOnly, |c| c.domain.write_policy.get()), - ), + write_policy: cell::Cell::new(wp), origin, dyn_root: OnceCell::new(), dirty: cell::Cell::new(false), @@ -636,6 +636,17 @@ impl Xell { /// cell is the domain root. HErrKind::None is never returned. /// The path is returned as a string of labels separated by slashes. pub fn path(&self) -> Res { + if let DynCell::Error(err) = &self.dyn_cell { + return Ok(String::new()); + } + + fn err_to_string(e: HErr) -> String { + if e.kind == HErrKind::None { + return String::new(); + } + format!("<💥 {}>", e) + } + let mut v: Vec = Vec::new(); let mut some_orig = Some(self.clone()); let mut iteration = 0; @@ -650,13 +661,16 @@ impl Xell { let base_str = if cell.domain.origin.is_some() { format!("{}{}", Relation::Interpretation, cell.interpretation()) } else { - let mut s = format!( - "{}", - cell.read() - .value() - .unwrap_or(Value::Str("<💥 cell read error>")) - ) - .replace('\n', "\\n"); + // avoid Xell::read which may call path again + let mut s = dispatch_dyn_cell!(&cell.dyn_cell, |x| { + match x.read() { + Ok(r) => match r.value() { + Ok(v) => v.as_cow_str().as_ref().replace('\n', "\\n"), + Err(e) => err_to_string(e), + }, + Err(e) => err_to_string(e), + } + }); if s.len() > 16 { s.truncate(16); s += "..."; @@ -763,7 +777,7 @@ impl Drop for Domain { } let target = guard_some!(self.origin.as_ref(), { return }); let dyn_root = guard_some!(self.dyn_root.get(), { - warning!("❗️root of dirty domain not found"); + warning!("root of dirty domain not found"); return; }); @@ -778,13 +792,13 @@ fn test_cell_domain_path() -> Res<()> { let tree = r#"{"a": {"x": "xa", "b": {"x": "xb", "c": {"x": "xc"}}}, "m": [1, 2, 3]}"#; let root = Xell::from(tree).be("yaml"); - assert_eq!(root.domain_path()?, r#""#); - let leaf = root.to("/a/b/c/x"); - assert_eq!(leaf.domain_path()?, r#"/a/b/c/x"#); + // assert_eq!(root.domain_path()?, r#""#); + // let leaf = root.to("/a/b/c/x"); + // assert_eq!(leaf.domain_path()?, r#"/a/b/c/x"#); assert_eq!(root.path()?, r#"`{"a": {"x": "xa"...`^yaml"#); - assert_eq!(leaf.path()?, r#"`{"a": {"x": "xa"...`^yaml/a/b/c/x"#); + // assert_eq!(leaf.path()?, r#"`{"a": {"x": "xa"...`^yaml/a/b/c/x"#); Ok(()) } diff --git a/src/interpretations/http.rs b/src/interpretations/http.rs index edfd028..f81039e 100644 --- a/src/interpretations/http.rs +++ b/src/interpretations/http.rs @@ -98,7 +98,6 @@ impl Cell { } else { request }; - println!("http call request {:?}", request); let response = request.send()?; let mut headers = IndexMap::>::new(); @@ -119,7 +118,6 @@ impl Cell { if status >= 400 { warning!("Error: http call failed: {} = {} {}", url, status, reason); } - println!("http call response {:?}", response); let response = OwnRc::new(Response { status, reason, diff --git a/src/prog/mod.rs b/src/prog/mod.rs index 9d4e41a..6856f33 100644 --- a/src/prog/mod.rs +++ b/src/prog/mod.rs @@ -18,7 +18,7 @@ fn convert_error(input: &str, err: nom::Err>) -> String { nom::Err::Incomplete(needed) => { format!("path parsing failed, more input needed {:?}", needed) } - nom::Err::Error(e) => nom::error::convert_error(input, e), - nom::Err::Failure(e) => nom::error::convert_error(input, e), + nom::Err::Error(e) => format!("parse error: {}", nom::error::convert_error(input, e)), + nom::Err::Failure(e) => format!("parse error: {}", nom::error::convert_error(input, e)), } } diff --git a/src/prog/parse_path.rs b/src/prog/parse_path.rs index d4ccc12..d04857d 100644 --- a/src/prog/parse_path.rs +++ b/src/prog/parse_path.rs @@ -9,11 +9,9 @@ use nom::{ bytes::complete::{escaped, tag, take_till}, character::complete::{anychar, digit1, none_of, one_of, space0}, combinator::{all_consuming, opt, recognize}, - error::VerboseErrorKind, - error::{context, VerboseError}, - multi::separated_list1, - multi::{many0, many1}, - sequence::{delimited, terminated, tuple}, + error::{context, VerboseError, VerboseErrorKind}, + multi::{many0, many1, separated_list0, separated_list1}, + sequence::{delimited, tuple}, }; use std::str::{from_utf8, FromStr}; @@ -30,10 +28,8 @@ pub fn parse_path_with_starter(input: &str) -> Res<(PathStart, Path)> { } pub fn path_with_starter(input: &str) -> NomRes<&str, (PathStart, Path)> { - context("path", tuple((space0, path_start, path_items)))(input).map(|(next_input, res)| { - let (_, start, path) = res; - (next_input, (start, path)) - }) + context("path", tuple((path_start, space0, path_items)))(input) + .map(|(next_input, res)| (next_input, (res.0, res.2))) } fn path_start(input: &str) -> NomRes<&str, PathStart> { @@ -52,16 +48,12 @@ fn path_start_file(input: &str) -> NomRes<&str, PathStart> { "path_start_file", tuple(( alt((tag("/"), tag("."), tag("~"))), - many0(terminated(path_code_points, tag("/"))), - opt(path_code_points), + separated_list0(tag("/"), path_code_points), )), )(input) .map(|(next_input, res)| { let mut len: usize = res.0.len(); len += res.1.into_iter().map(|s| s.len() + 1).sum::(); - if let Some(last) = res.2 { - len += last.len(); - } (next_input, PathStart::File(input[0..len].to_string())) }) } @@ -240,7 +232,7 @@ fn relation(input: &str) -> NomRes<&str, char> { .map(|(next_input, res)| (next_input, res.chars().next().unwrap())) } -fn rvalue(input: &str) -> NomRes<&str, OwnValue> { +pub(super) fn rvalue(input: &str) -> NomRes<&str, OwnValue> { context("value", alt((value_string, value_uint, value_ident)))(input) } @@ -254,7 +246,7 @@ fn value_string(input: &str) -> NomRes<&str, OwnValue> { .map(|(next_input, res)| (next_input, OwnValue::String(res))) } -fn value_uint(input: &str) -> NomRes<&str, OwnValue> { +pub(super) fn value_uint(input: &str) -> NomRes<&str, OwnValue> { context("value_uint", digit1)(input) .and_then(|(next_input, res)| match res.parse::() { Ok(n) => Ok((next_input, n)), diff --git a/src/prog/parse_program.rs b/src/prog/parse_program.rs index 63833b8..ee7ab76 100644 --- a/src/prog/parse_program.rs +++ b/src/prog/parse_program.rs @@ -4,8 +4,8 @@ use crate::{ prog::{parse_path::*, program::*, *}, }; use nom::{ - character::complete::space0, combinator::all_consuming, error::context, multi::many0, - sequence::terminated, + branch::alt, bytes::complete::tag, character::complete::space0, combinator::all_consuming, + error::context, multi::separated_list0, sequence::tuple, }; pub fn parse_program(input: &str) -> Res { @@ -17,13 +17,29 @@ pub fn parse_program(input: &str) -> Res { } fn program(input: &str) -> NomRes<&str, Program> { - context("program", many0(statement))(input).map(|(next_input, res)| { + context( + "program", + separated_list0(tuple((space0, tag(";"), space0)), statement), + )(input) + .map(|(next_input, res)| { let statements = res.iter().map(|p| p.to_owned()).collect(); (next_input, Program(statements)) }) } fn statement(input: &str) -> NomRes<&str, Statement> { - context("statement", terminated(path_with_starter, space0))(input) - .map(|(next_input, res)| (next_input, Statement::PathWithStart(res.0, res.1))) + context("statement", alt((statement_assignment, statement_path)))(input) +} + +fn statement_path(input: &str) -> NomRes<&str, Statement> { + context("path", path_with_starter)(input) + .map(|(next_input, res)| (next_input, Statement::Path(res.0, res.1))) +} + +fn statement_assignment(input: &str) -> NomRes<&str, Statement> { + context( + "assignment", + tuple((path_with_starter, space0, tag("="), space0, value_uint)), + )(input) + .map(|(next_input, res)| (next_input, Statement::Assignment(res.0 .0, res.0 .1, res.4))) } diff --git a/src/prog/path.rs b/src/prog/path.rs index 7361923..bbbd5a1 100644 --- a/src/prog/path.rs +++ b/src/prog/path.rs @@ -203,10 +203,12 @@ impl<'a> PathStart<'a> { impl<'a> Path<'a> { pub fn parse(input: &str) -> Res { + let input = input.trim(); super::parse_path::parse_path(input) } pub fn parse_with_starter(input: &str) -> Res<(PathStart, Path)> { + let input = input.trim(); super::parse_path::parse_path_with_starter(input) } diff --git a/src/prog/program.rs b/src/prog/program.rs index 02269db..84464ba 100644 --- a/src/prog/program.rs +++ b/src/prog/program.rs @@ -14,7 +14,7 @@ macro_rules! ifdebug { #[derive(Clone, Debug)] pub struct Program<'a>(pub(crate) Vec>); -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct ProgramParams { pub print_depth: usize, pub print_breadth: usize, @@ -22,7 +22,8 @@ pub struct ProgramParams { #[derive(Clone, Debug)] pub enum Statement<'a> { - PathWithStart(PathStart<'a>, Path<'a>), + Path(PathStart<'a>, Path<'a>), + Assignment(PathStart<'a>, Path<'a>, OwnValue), } #[derive(Clone, Debug)] @@ -44,7 +45,10 @@ impl<'a> Display for Program<'a> { impl<'a> Display for Statement<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - Statement::PathWithStart(start, path) => write!(f, "{}{}", start, path)?, + Statement::Path(start, path) => write!(f, "{}{}", start, path)?, + Statement::Assignment(start, path, value) => { + write!(f, "{}{} = {}", start, path, value)? + } } Ok(()) } @@ -52,6 +56,7 @@ impl<'a> Display for Statement<'a> { impl<'a> Program<'a> { pub fn parse(input: &str) -> Res { + let input = input.trim(); super::parse_program::parse_program(input) } @@ -59,21 +64,19 @@ impl<'a> Program<'a> { for statement in &self.0 { debug!("Running statement: {}", statement); match statement { - Statement::PathWithStart(start, path) => { + Statement::Assignment(start, path, value) => { + ifdebug!(println!("-- Assignment: {}{} = {}", start, path, value)); + let searcher = Searcher::new(start.eval()?, path.clone()); + for cell in searcher { + cell?.write().value(value.clone())?; + } + } + Statement::Path(start, path) => { ifdebug!(println!("-- PathWithStart: {} {}", start, path)); - let root = start.eval()?; - let mut searcher = Searcher::new(root, path.clone()); - let Some(rescell) = searcher.next() else { - continue; - }; - match rescell { - Ok(cell) => { - pprint(&cell, params.print_depth, params.print_breadth); - } - Err(e) => { - eprintln!("Error: {}", e); - } - }; + let searcher = Searcher::new(start.eval()?, path.clone()); + for cell in searcher { + pprint(&cell?, params.print_depth, params.print_breadth); + } } } } diff --git a/src/prog/searcher.rs b/src/prog/searcher.rs index 2c276c0..3e107fa 100644 --- a/src/prog/searcher.rs +++ b/src/prog/searcher.rs @@ -205,7 +205,7 @@ impl<'s> Searcher<'s> { _ => return Some(userres("bad interpretation selector")), }; let itp_cell = guard_ok!(itp_cell.err(), err => { - println!("Error while searching: cannot get interpretation cell: {:?}", err); + ifdebug!(println!("no such interpretation: {:?}", err)); return Some(Err(err)); }); if !epi.params.is_empty() { diff --git a/src/tests/data/assignment.json b/src/tests/data/assignment.json new file mode 100644 index 0000000..6789fb1 --- /dev/null +++ b/src/tests/data/assignment.json @@ -0,0 +1,3 @@ +{ + "a": 1 +} \ No newline at end of file diff --git a/examples/write.json b/src/tests/data/write.json similarity index 100% rename from examples/write.json rename to src/tests/data/write.json diff --git a/examples/write.txt b/src/tests/data/write.txt similarity index 100% rename from examples/write.txt rename to src/tests/data/write.txt diff --git a/examples/write2.txt b/src/tests/data/write2.txt similarity index 100% rename from examples/write2.txt rename to src/tests/data/write2.txt diff --git a/examples/write3.txt b/src/tests/data/write3.txt similarity index 100% rename from examples/write3.txt rename to src/tests/data/write3.txt diff --git a/src/tests/fs.rs b/src/tests/fs.rs index 3a11a1e..6d70a66 100644 --- a/src/tests/fs.rs +++ b/src/tests/fs.rs @@ -9,6 +9,15 @@ fn test_files() -> Res<()> { Ok(()) } +#[test] +fn file_not_found() -> Res<()> { + crate::utils::log::set_verbose(true); + let examples = Xell::new("./src/tests/data/assignment.jso"); + assert_eq!(examples.clone().err().unwrap_err().kind, HErrKind::None); + println!("{:?}", examples.read()); + Ok(()) +} + #[test] fn test_file_path_tilde() -> Res<()> { crate::utils::log::set_verbose(true); @@ -44,7 +53,7 @@ fn search_path_with_fs_starter() -> Res<()> { #[test] fn fs_write_prog_policy() -> Res<()> { - let p = "^path^fs/examples/write.txt"; + let p = "^path^fs/src/tests/data/write.txt"; let c = Xell::from(".") .policy(WritePolicy::NoAutoWrite) .to(p) @@ -64,7 +73,7 @@ fn fs_write_prog_policy() -> Res<()> { #[test] fn fs_write_path_policy() -> Res<()> { - let p = ".^fs[w]/examples/write2.txt"; + let p = ".^fs[w]/src/tests/data/write2.txt"; let c = Xell::new(p).err()?; c.write().value("Hi there")?; assert_eq!( @@ -78,15 +87,23 @@ fn fs_write_path_policy() -> Res<()> { #[test] fn fs_path() -> Res<()> { - let c = Xell::from(".").be("path").be("fs").sub().get("examples"); - assert_eq!(c.path()?, "`.`^path^fs/examples"); + let c = Xell::from(".") + .be("path") + .be("fs") + .sub() + .get("src") + .sub() + .get("tests") + .sub() + .get("data"); + assert_eq!(c.path()?, "`.`^path^fs/src/tests/data"); Ok(()) } #[test] fn fs_write_beyond_fs() -> Res<()> { // test that the fs drop will not try to write back to the path cell itself - let ov = Xell::from("./examples/write3.txt").policy(WritePolicy::WriteBackOnDrop); + let ov = Xell::from("./src/tests/data/write3.txt").policy(WritePolicy::WriteBackOnDrop); { ov.be("path").be("fs").write().value("A")?; ov.be("path").be("fs").write().value("B")?; diff --git a/src/tests/ideals.rs b/src/tests/ideals.rs deleted file mode 100644 index 9c8f93a..0000000 --- a/src/tests/ideals.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::{api::*, pprint, utils::log::set_verbose}; - -const TREE: &str = r#" -a: - x: xa - b: - x: xb - b: - x: xc - b: bval -m: mval -n: nval -"#; - -#[test] -fn tree() -> Res<()> { - set_verbose(true); - - let cell = Xell::from("./examples/write.json"); - pprint(&cell, 0, 0); - // let cell = cell.to("^path^fs[w]^json"); - // pprint(&cell, 0, 0); - // assert!(cell.clone().err().is_ok()); - // assert!(cell.write().value("weak as putty".into()).is_ok()); - - // let cell = Cell::from(".") - // .to("^path^fs[w]/examples/productiondump.json") - // .to("^json/stacks/*/dockerCompose") - // .to("^yaml/services/scheduler/image") - // .to("^split(':')/[-1]") - // .err()?; - - // assert_eq!( - // cell.origin().to(cell.path()?.as_str()).read().value()?, - // "0.8.6" - // ); - - // // this should set the docker image tag specified in the docker compose string - // // embedded in the json from the productiondump json file - // cell.origin() - // .policy(WritePolicy::WriteBackOnDrop) - // .to(cell.path()?.as_str()) - // .write() - // .value("0.8.8".into())?; - - // assert_eq!( - // cell.origin().to(cell.path()?.as_str()).read().value()?, - // "0.8.8" - // ); - - Ok(()) -} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 66832dc..48e5c1c 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,10 +1,10 @@ mod fs; mod http; -mod ideals; mod json; mod nested; mod path; mod perf; +mod program; mod regex; mod rust; mod search; diff --git a/src/tests/program.rs b/src/tests/program.rs new file mode 100644 index 0000000..cea0836 --- /dev/null +++ b/src/tests/program.rs @@ -0,0 +1,47 @@ +use crate::{ + api::*, + prog::{program::Statement, Path, PathStart, Program, ProgramParams}, + utils::log::set_verbose, +}; + +#[test] +fn program_simple_path() -> Res<()> { + set_verbose(true); + let prog = Program::parse(".^regex[a] ")?; + match &prog.0[0] { + Statement::Path(start, path) => { + assert_eq!(start, &PathStart::File(".".to_string())); + assert_eq!(path, &Path::parse("^regex[a]")?); + } + Statement::Assignment(_, _, _) => panic!("Expected a path statement!"), + } + Ok(()) +} + +#[test] +fn program_simple_assign() -> Res<()> { + set_verbose(true); + assert_eq!( + Xell::new("./src/tests/data/assignment.json^json/a") + .read() + .value()?, + Int::from(1) + ); + Program::parse("./src/tests/data/assignment.json^fs[w]^json/a = 2")? + .run(ProgramParams::default())?; + assert_eq!( + Xell::new("./src/tests/data/assignment.json^json/a") + .read() + .value()?, + Int::from(2) + ); + Program::parse("./src/tests/data/assignment.json^fs[w]^json/a = 1")? + .run(ProgramParams::default())?; + assert_eq!( + Xell::new("./src/tests/data/assignment.json^json/a") + .read() + .value()?, + Int::from(1) + ); + Ok(()) +} diff --git a/src/tests/search.rs b/src/tests/search.rs index cd29c05..c0d153c 100644 --- a/src/tests/search.rs +++ b/src/tests/search.rs @@ -1,32 +1,15 @@ use crate::{ api::*, pprint, - prog::{ - path::{ - ElevationPathItem, Expression, Filter, InterpretationParam, NormalPathItem, Path, - PathItem, - }, - program::Statement, - PathStart, Program, + prog::path::{ + ElevationPathItem, Expression, Filter, InterpretationParam, NormalPathItem, Path, PathItem, }, utils::log::set_verbose, }; -#[test] -fn path_simple_program() -> Res<()> { - let prog = Program::parse(".^regex[a] ")?; - match &prog.0[0] { - Statement::PathWithStart(start, path) => { - assert_eq!(start, &PathStart::File(".".to_string())); - assert_eq!(path, &Path::parse("^regex[a]")?); - } - } - Ok(()) -} - #[test] fn path_simple_elevation() -> Res<()> { - let path = Path::parse("^fs^fs.one[w]^fs.two[w=1] ")?; + let path = Path::parse(" ^fs^fs.one[w]^fs.two[w=1] ")?; assert_eq!( path.0.as_slice(), &[ diff --git a/src/utils/log.rs b/src/utils/log.rs index 470d483..32401fb 100644 --- a/src/utils/log.rs +++ b/src/utils/log.rs @@ -4,7 +4,7 @@ pub static VERBOSE: AtomicBool = AtomicBool::new(false); #[macro_export] macro_rules! warning { - ( $($arg:tt)* ) => ( eprintln!("‣ {}", format!($($arg)*)) ); + ( $($arg:tt)* ) => ( eprintln!("⚠ {}", format!($($arg)*)) ); } #[macro_export]