Skip to content

fix typos and warnings #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 151 additions & 24 deletions src/json_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ use std::fmt::Debug;
#[grammar = "grammer.pest"]
pub struct JsonPathParser;

#[derive(Debug, PartialEq)]
pub enum JsonPathToken {
String,
Number,
}

#[derive(Debug)]
pub struct Query<'i> {
// query: QueryElement<'i>
pub query: Pairs<'i, Rule>,
pub root: Pairs<'i, Rule>,
is_static: Option<bool>,
size: Option<usize>,
}

#[derive(Debug)]
Expand All @@ -21,11 +29,66 @@ pub struct QueryCompilationError {
message: String,
}

impl<'i> Query<'i> {
pub fn pop_last(&mut self) -> Option<(String, JsonPathToken)> {
let last = self.root.next_back();
match last {
Some(last) => match last.as_rule() {
Rule::literal => Some((last.as_str().to_string(), JsonPathToken::String)),
Rule::number => Some((last.as_str().to_string(), JsonPathToken::Number)),
Rule::numbers_list => {
let first_on_list = last.into_inner().next();
first_on_list.map(|first| (first.as_str().to_string(), JsonPathToken::Number))
}
Rule::string_list => {
let first_on_list = last.into_inner().next();
first_on_list.map(|first| (first.as_str().to_string(), JsonPathToken::String))
}
_ => panic!("pop last was used in a none static path"),
},
None => None,
}
}

pub fn size(&mut self) -> usize {
if self.size.is_some() {
return *self.size.as_ref().unwrap();
}
self.is_static();
self.size()
}

pub fn is_static(&mut self) -> bool {
if let Some(s) = self.is_static {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this will consume the option value and will leave it None?

return s;
}
let mut size = 0;
let mut is_static = true;
let root_copy = self.root.clone();
for n in root_copy {
size += 1;
match n.as_rule() {
Rule::literal | Rule::number => continue,
Rule::numbers_list | Rule::string_list => {
let inner = n.into_inner();
if inner.count() > 1 {
is_static = false;
}
}
_ => is_static = false,
}
}
self.size = Some(size);
self.is_static = Some(is_static);
self.is_static()
}
}

impl std::fmt::Display for QueryCompilationError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(
f,
"Error accured on possition {}, {}",
"Error occurred on position {}, {}",
self.location, self.message
)
}
Expand All @@ -50,7 +113,14 @@ impl std::fmt::Display for Rule {
pub(crate) fn compile(path: &str) -> Result<Query, QueryCompilationError> {
let query = JsonPathParser::parse(Rule::query, path);
match query {
Ok(q) => Ok(Query { query: q }),
Ok(mut q) => {
let root = q.next().unwrap();
Ok(Query {
root: root.into_inner(),
is_static: None,
size: None,
})
}
// pest::error::Error
Err(e) => {
let pos = match e.location {
Expand Down Expand Up @@ -147,19 +217,19 @@ pub enum PTrackerElement {

#[derive(Debug, PartialEq)]
pub struct PTracker {
pub elemenets: Vec<PTrackerElement>,
pub elements: Vec<PTrackerElement>,
}
impl UserPathTracker for PTracker {
fn add_str(&mut self, s: &str) {
self.elemenets.push(PTrackerElement::Key(s.to_string()));
self.elements.push(PTrackerElement::Key(s.to_string()));
}

fn add_index(&mut self, i: usize) {
self.elemenets.push(PTrackerElement::Index(i));
self.elements.push(PTrackerElement::Index(i));
}

fn to_string_path(self) -> Vec<String> {
self.elemenets
self.elements
.into_iter()
.map(|e| match e {
PTrackerElement::Key(s) => s,
Expand All @@ -174,7 +244,7 @@ impl UserPathTrackerGenerator for PTrackerGenerator {
type PT = PTracker;
fn generate(&self) -> Self::PT {
PTracker {
elemenets: Vec::new(),
elements: Vec::new(),
}
}
}
Expand All @@ -199,7 +269,10 @@ const fn create_empty_trucker<'i, 'j>() -> PathTracker<'i, 'j> {
}
}

const fn create_str_trucker<'i, 'j>(s: &'i str, father: &'j PathTracker<'i, 'j>) -> PathTracker<'i, 'j> {
const fn create_str_trucker<'i, 'j>(
s: &'i str,
father: &'j PathTracker<'i, 'j>,
) -> PathTracker<'i, 'j> {
PathTracker {
father: Some(father),
element: PathTrackerElement::Key(s),
Expand Down Expand Up @@ -228,7 +301,7 @@ enum TermEvaluationResult<'i, 'j, S: SelectValue> {

enum CmpResult {
Ord(Ordering),
NotCmparable,
NotComparable,
}

impl<'i, 'j, S: SelectValue> TermEvaluationResult<'i, 'j, S> {
Expand Down Expand Up @@ -268,45 +341,45 @@ impl<'i, 'j, S: SelectValue> TermEvaluationResult<'i, 'j, S> {
SelectValueType::Long => TermEvaluationResult::Integer(v.get_long()).cmp(s),
SelectValueType::Double => TermEvaluationResult::Float(v.get_double()).cmp(s),
SelectValueType::String => TermEvaluationResult::Str(v.as_str()).cmp(s),
_ => CmpResult::NotCmparable,
_ => CmpResult::NotComparable,
},
(_, TermEvaluationResult::Value(v)) => match v.get_type() {
SelectValueType::Long => self.cmp(&TermEvaluationResult::Integer(v.get_long())),
SelectValueType::Double => self.cmp(&TermEvaluationResult::Float(v.get_double())),
SelectValueType::String => self.cmp(&TermEvaluationResult::Str(v.as_str())),
_ => CmpResult::NotCmparable,
_ => CmpResult::NotComparable,
},
(TermEvaluationResult::Invalid, _) | (_, TermEvaluationResult::Invalid) => {
CmpResult::NotCmparable
CmpResult::NotComparable
}
(_, _) => CmpResult::NotCmparable,
(_, _) => CmpResult::NotComparable,
}
}
fn gt(&self, s: &Self) -> bool {
match self.cmp(s) {
CmpResult::Ord(o) => o.is_gt(),
CmpResult::NotCmparable => false,
CmpResult::NotComparable => false,
}
}

fn ge(&self, s: &Self) -> bool {
match self.cmp(s) {
CmpResult::Ord(o) => o.is_ge(),
CmpResult::NotCmparable => false,
CmpResult::NotComparable => false,
}
}

fn lt(&self, s: &Self) -> bool {
match self.cmp(s) {
CmpResult::Ord(o) => o.is_lt(),
CmpResult::NotCmparable => false,
CmpResult::NotComparable => false,
}
}

fn le(&self, s: &Self) -> bool {
match self.cmp(s) {
CmpResult::Ord(o) => o.is_le(),
CmpResult::NotCmparable => false,
CmpResult::NotComparable => false,
}
}

Expand All @@ -325,7 +398,7 @@ impl<'i, 'j, S: SelectValue> TermEvaluationResult<'i, 'j, S> {
(TermEvaluationResult::Value(v1), TermEvaluationResult::Value(v2)) => v1 == v2,
(_, _) => match self.cmp(s) {
CmpResult::Ord(o) => o.is_eq(),
CmpResult::NotCmparable => false,
CmpResult::NotComparable => false,
},
}
}
Expand Down Expand Up @@ -836,7 +909,7 @@ impl<'i, UPTG: UserPathTrackerGenerator> PathCalculator<'i, UPTG> {
Rule::filter => {
if flat_arrays_on_filter && json.get_type() == SelectValueType::Array {
/* lets expend the array, this is how most json path engines work.
* Pesonally, I think this if should not exists. */
* Personally, I think this if should not exists. */
let values = json.values().unwrap();
if let Some(pt) = path_tracker {
for (i, v) in values.enumerate() {
Expand Down Expand Up @@ -883,22 +956,22 @@ impl<'i, UPTG: UserPathTrackerGenerator> PathCalculator<'i, UPTG> {
pub fn calc_with_paths_on_root<'j: 'i, S: SelectValue>(
&self,
json: &'j S,
root: Pair<Rule>,
root: Pairs<'i, Rule>,
) -> Vec<CalculationResult<'j, S, UPTG::PT>> {
let mut calc_data = PathCalculatorData {
results: Vec::new(),
root: json,
};
if self.tracker_generator.is_some() {
self.calc_internal(
root.into_inner(),
root,
json,
Some(create_empty_trucker()),
&mut calc_data,
true,
);
} else {
self.calc_internal(root.into_inner(), json, None, &mut calc_data, true);
self.calc_internal(root, json, None, &mut calc_data, true);
}
calc_data.results.drain(..).collect()
}
Expand All @@ -907,7 +980,7 @@ impl<'i, UPTG: UserPathTrackerGenerator> PathCalculator<'i, UPTG> {
&self,
json: &'j S,
) -> Vec<CalculationResult<'j, S, UPTG::PT>> {
self.calc_with_paths_on_root(json, self.query.unwrap().query.clone().next().unwrap())
self.calc_with_paths_on_root(json, self.query.unwrap().root.clone())
}

pub fn calc<'j: 'i, S: SelectValue>(&self, json: &'j S) -> Vec<&'j S> {
Expand All @@ -924,3 +997,57 @@ impl<'i, UPTG: UserPathTrackerGenerator> PathCalculator<'i, UPTG> {
.collect()
}
}

#[cfg(test)]
mod json_path_compiler_tests {
use crate::json_path::compile;
use crate::json_path::JsonPathToken;

#[test]
fn test_compiler_pop_last() {
let query = compile("$.foo");
assert_eq!(
query.unwrap().pop_last().unwrap(),
("foo".to_string(), JsonPathToken::String)
);
}

#[test]
fn test_compiler_pop_last_number() {
let query = compile("$.[1]");
assert_eq!(
query.unwrap().pop_last().unwrap(),
("1".to_string(), JsonPathToken::Number)
);
}

#[test]
fn test_compiler_pop_last_string_bracket_notation() {
let query = compile("$.[\"foo\"]");
assert_eq!(
query.unwrap().pop_last().unwrap(),
("foo".to_string(), JsonPathToken::String)
);
}

#[test]
fn test_compiler_is_static() {
let query = compile("$.[\"foo\"]");
assert!(query.unwrap().is_static());

let query = compile("$.[\"foo\", \"bar\"]");
assert!(!query.unwrap().is_static());
}

#[test]
fn test_compiler_size() {
let query = compile("$.[\"foo\"]");
assert_eq!(query.unwrap().size(), 1);

let query = compile("$.[\"foo\"].bar");
assert_eq!(query.unwrap().size(), 2);

let query = compile("$.[\"foo\"].bar[1]");
assert_eq!(query.unwrap().size(), 3);
}
}
20 changes: 10 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ pub fn compile(s: &str) -> Result<Query, QueryCompilationError> {
json_path::compile(s)
}

/// Calc once allows to performe a one time calculation on the give query.
/// Calc once allows to perform a one time calculation on the give query.
/// The query ownership is taken so it can not be used after. This allows
/// the get a better performance if there is a need to calculate the query
/// only once.
pub fn calc_once<'j, 'p, S: SelectValue>(mut q: Query<'j>, json: &'p S) -> Vec<&'p S> {
let root = q.query.next().unwrap();
pub fn calc_once<'j, 'p, S: SelectValue>(q: Query<'j>, json: &'p S) -> Vec<&'p S> {
let root = q.root;
PathCalculator::<'p, DummyTrackerGenerator> {
query: None,
tracker_generator: None,
Expand All @@ -77,10 +77,10 @@ pub fn calc_once<'j, 'p, S: SelectValue>(mut q: Query<'j>, json: &'p S) -> Vec<&

/// A version of `calc_once` that returns also paths.
pub fn calc_once_with_paths<'j, 'p, S: SelectValue>(
mut q: Query<'j>,
q: Query<'j>,
json: &'p S,
) -> Vec<CalculationResult<'p, S, PTracker>> {
let root = q.query.next().unwrap();
let root = q.root;
PathCalculator {
query: None,
tracker_generator: Some(PTrackerGenerator),
Expand All @@ -89,8 +89,8 @@ pub fn calc_once_with_paths<'j, 'p, S: SelectValue>(
}

/// A version of `calc_once` that returns only paths as Vec<Vec<String>>.
pub fn calc_once_paths<S: SelectValue>(mut q: Query, json: &S) -> Vec<Vec<String>> {
let root = q.query.next().unwrap();
pub fn calc_once_paths<S: SelectValue>(q: Query, json: &S) -> Vec<Vec<String>> {
let root = q.root;
PathCalculator {
query: None,
tracker_generator: Some(PTrackerGenerator),
Expand All @@ -115,7 +115,7 @@ mod json_path_tests {
path_calculator.calc(json)
}

fn perform_path_search<'a>(path: &str, json: &'a Value) -> Vec<Vec<String>> {
fn perform_path_search(path: &str, json: &Value) -> Vec<Vec<String>> {
let query = crate::compile(path).unwrap();
let path_calculator = crate::create_with_generator(&query);
path_calculator.calc_paths(json)
Expand All @@ -142,9 +142,9 @@ mod json_path_tests {
) => {
let j = json!($json);
let res = perform_path_search($path, &j);
let mut v = Vec::new();
let mut v = vec![];
$(
let mut s = Vec::new();
let mut s = vec![];
$(
s.push(stringify!($result));
)*
Expand Down