use std::fmt::{Debug, Write};
use std::ops::Range;
use crate::jsonparser;
use crate::lineprinter;
use crate::yamlparser;
pub type Index = usize;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum OptionIndex {
Nil,
Index(Index),
}
impl OptionIndex {
pub fn is_nil(&self) -> bool {
matches!(self, OptionIndex::Nil)
}
pub fn is_some(&self) -> bool {
!self.is_nil()
}
pub fn unwrap(&self) -> Index {
match self {
OptionIndex::Nil => panic!("Called .unwrap() on Nil OptionIndex"),
OptionIndex::Index(i) => *i,
}
}
}
pub const NIL: usize = usize::MAX;
impl From<usize> for OptionIndex {
fn from(i: usize) -> Self {
if i == NIL {
OptionIndex::Nil
} else {
OptionIndex::Index(i)
}
}
}
#[derive(PartialEq, Copy, Clone)]
pub enum PathType {
Dot,
Bracket,
Query,
DotWithTopLevelIndex,
}
#[derive(Debug)]
pub struct FlatJson(
pub Vec<Row>,
pub String,
pub usize,
);
impl FlatJson {
pub fn last_visible_index(&self) -> Index {
let last_index = self.0.len() - 1;
let row = &self.0[last_index];
if row.is_container() && row.is_collapsed() {
row.pair_index().unwrap()
} else {
last_index
}
}
pub fn last_visible_item(&self) -> Index {
let mut last_index = self.0.len() - 1;
loop {
let row = &self.0[last_index];
if row.is_primitive() {
return last_index;
}
if row.is_closing_of_container() && row.is_collapsed() {
return row.pair_index().unwrap();
}
last_index -= 1;
}
}
pub fn prev_visible_row(&self, index: Index) -> OptionIndex {
if index == 0 {
return OptionIndex::Nil;
}
let row = &self.0[index - 1];
if row.is_closing_of_container() && row.is_collapsed() {
row.pair_index()
} else {
OptionIndex::Index(index - 1)
}
}
pub fn next_visible_row(&self, mut index: Index) -> OptionIndex {
if self.0[index].is_opening_of_container() && self.0[index].is_collapsed() {
index = self.0[index].pair_index().unwrap();
}
if index == self.0.len() - 1 {
return OptionIndex::Nil;
}
OptionIndex::Index(index + 1)
}
pub fn prev_item(&self, mut index: Index) -> OptionIndex {
while let OptionIndex::Index(i) = self.prev_visible_row(index) {
if !self.0[i].is_closing_of_container() {
return OptionIndex::Index(i);
}
index = i;
}
OptionIndex::Nil
}
pub fn next_item(&self, mut index: Index) -> OptionIndex {
while let OptionIndex::Index(i) = self.next_visible_row(index) {
if !self.0[i].is_closing_of_container() {
return OptionIndex::Index(i);
}
index = i;
}
OptionIndex::Nil
}
pub fn expand(&mut self, index: Index) {
if let OptionIndex::Index(pair) = self.0[index].pair_index() {
self.0[pair].expand();
}
self.0[index].expand();
}
pub fn collapse(&mut self, index: Index) {
if let OptionIndex::Index(pair) = self.0[index].pair_index() {
self.0[pair].collapse();
}
self.0[index].collapse();
}
pub fn toggle_collapsed(&mut self, index: Index) {
if let OptionIndex::Index(pair) = self.0[index].pair_index() {
self.0[pair].toggle_collapsed();
}
self.0[index].toggle_collapsed();
}
pub fn first_visible_ancestor(&self, mut index: Index) -> Index {
let mut visible_ancestor = index;
while let OptionIndex::Index(parent) = self[index].parent {
if self[parent].is_collapsed() {
visible_ancestor = parent;
}
index = parent;
}
visible_ancestor
}
pub fn build_path_to_node(&self, path_type: PathType, index: Index) -> Result<String, String> {
let mut buf = String::new();
if self[index].parent.is_nil() {
match path_type {
PathType::Dot | PathType::Bracket => {
return Err("Cannot build path to top-level element".to_string());
}
PathType::Query => {
return Ok(".".to_string());
}
PathType::DotWithTopLevelIndex => { }
}
}
self.build_path_to_node_impl(path_type, index, &mut buf)?;
Ok(buf)
}
fn build_path_to_node_impl(
&self,
path_type: PathType,
index: Index,
buf: &mut String,
) -> Result<(), String> {
let row = &self[index];
if row.is_closing_of_container() {
return self.build_path_to_node_impl(path_type, row.pair_index().unwrap(), buf);
}
if let OptionIndex::Index(parent_index) = row.parent {
self.build_path_to_node_impl(path_type, parent_index, buf)?;
}
let res = if let Some(key_range) = &row.key_range {
let key_open_delimiter = &self.1[key_range.start..key_range.start + 1];
let key = &self.1[key_range.start + 1..key_range.end - 1];
if key_open_delimiter == "[" {
if path_type == PathType::Query {
return Err(
"Path to node contains non-string keys not supported in JSON".to_string(),
);
}
write!(buf, "[{}]", key)
} else {
if path_type != PathType::Bracket && lineprinter::JS_IDENTIFIER.is_match(key) {
write!(buf, ".{}", key)
} else {
if path_type == PathType::Query && row.depth == 1 {
write!(buf, ".[\"{}\"]", key)
} else {
write!(buf, "[\"{}\"]", key)
}
}
}
} else {
if row.parent.is_nil() {
if path_type == PathType::DotWithTopLevelIndex
&& (index != 0 || row.next_sibling.is_some())
{
write!(buf, "[{}]", row.index)
} else {
Ok(())
}
} else {
match path_type {
PathType::Query => {
if row.depth == 1 {
write!(buf, ".[]")
} else {
write!(buf, "[]")
}
}
_ => write!(buf, "[{}]", row.index),
}
}
};
res.map_err(|e| e.to_string())
}
pub fn pretty_printed(&self) -> Result<String, std::fmt::Error> {
let mut buf = String::new();
for row in self.0.iter() {
for _ in 0..row.depth {
write!(buf, " ")?;
}
if let Some(ref key_range) = row.key_range {
write!(buf, "{}: ", &self.1[key_range.clone()])?;
}
let mut trailing_comma = row.parent.is_some() && row.next_sibling.is_some();
if let Some(container_type) = row.value.container_type() {
if row.value.is_opening_of_container() {
write!(buf, "{}", container_type.open_str())?;
trailing_comma = false;
} else {
write!(buf, "{}", container_type.close_str())?;
trailing_comma = row.parent.is_some()
&& self[row.pair_index().unwrap()].next_sibling.is_some();
}
} else {
write!(buf, "{}", &self.1[row.range.clone()])?;
}
if trailing_comma {
write!(buf, ",")?;
}
writeln!(buf)?;
}
Ok(buf)
}
pub fn pretty_printed_value(&self, value_index: Index) -> Result<String, std::fmt::Error> {
if self[value_index].is_primitive() {
return Ok(self.1[self[value_index].range.clone()].to_string());
}
let mut buf = String::new();
let container_type = self[value_index].value.container_type().unwrap();
let depth_offset = self[value_index].depth;
let pair_index = self[value_index].pair_index().unwrap();
let start_index = value_index.min(pair_index);
let end_index = value_index.max(pair_index);
writeln!(buf, "{}", container_type.open_str())?;
for index in start_index + 1..end_index {
let row = &self[index];
for _ in 0..(row.depth - depth_offset) {
write!(buf, " ")?;
}
if let Some(ref key_range) = row.key_range {
write!(buf, "{}: ", &self.1[key_range.clone()])?;
}
let mut trailing_comma = row.parent.is_some() && row.next_sibling.is_some();
if let Some(container_type) = row.value.container_type() {
if row.value.is_opening_of_container() {
write!(buf, "{}", container_type.open_str())?;
trailing_comma = false;
} else {
write!(buf, "{}", container_type.close_str())?;
trailing_comma = row.parent.is_some()
&& self[row.pair_index().unwrap()].next_sibling.is_some();
}
} else {
write!(buf, "{}", &self.1[row.range.clone()])?;
}
if trailing_comma {
write!(buf, ",")?;
}
writeln!(buf)?;
}
writeln!(buf, "{}", container_type.close_str())?;
Ok(buf)
}
}
impl std::ops::Index<usize> for FlatJson {
type Output = Row;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
impl std::ops::IndexMut<usize> for FlatJson {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.0[index]
}
}
#[derive(Debug)]
pub struct Row {
pub parent: OptionIndex,
pub prev_sibling: OptionIndex,
pub next_sibling: OptionIndex,
pub depth: usize,
pub index: Index,
pub range: Range<usize>,
pub key_range: Option<Range<usize>>,
pub value: Value,
}
impl Row {
pub fn is_primitive(&self) -> bool {
self.value.is_primitive()
}
pub fn is_container(&self) -> bool {
self.value.is_container()
}
pub fn is_string(&self) -> bool {
self.value.is_string()
}
pub fn is_opening_of_container(&self) -> bool {
self.value.is_opening_of_container()
}
pub fn is_closing_of_container(&self) -> bool {
self.value.is_closing_of_container()
}
pub fn is_collapsed(&self) -> bool {
self.value.is_collapsed()
}
pub fn is_expanded(&self) -> bool {
self.value.is_expanded()
}
pub fn is_array(&self) -> bool {
self.value.is_array()
}
fn expand(&mut self) {
self.value.expand()
}
fn collapse(&mut self) {
self.value.collapse()
}
fn toggle_collapsed(&mut self) {
self.value.toggle_collapsed()
}
pub fn first_child(&self) -> OptionIndex {
self.value.first_child()
}
pub fn last_child(&self) -> OptionIndex {
self.value.last_child()
}
pub fn pair_index(&self) -> OptionIndex {
self.value.pair_index()
}
pub fn full_range(&self) -> Range<usize> {
match &self.key_range {
Some(key_range) => key_range.start..self.range.end,
None => self.range.clone(),
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum ContainerType {
Object,
Array,
}
impl ContainerType {
pub fn open_str(&self) -> &'static str {
match self {
ContainerType::Object => "{",
ContainerType::Array => "[",
}
}
pub fn close_str(&self) -> &'static str {
match self {
ContainerType::Object => "}",
ContainerType::Array => "]",
}
}
pub fn collapsed_preview(&self) -> &'static str {
match self {
ContainerType::Object => "{…}",
ContainerType::Array => "[…]",
}
}
}
#[derive(Debug)]
pub enum Value {
Null,
Boolean,
Number,
String,
EmptyObject,
EmptyArray,
OpenContainer {
container_type: ContainerType,
collapsed: bool,
first_child: Index,
close_index: Index,
},
CloseContainer {
container_type: ContainerType,
collapsed: bool,
last_child: Index,
open_index: Index,
},
}
impl Value {
pub fn is_primitive(&self) -> bool {
!self.is_container()
}
pub fn is_container(&self) -> bool {
matches!(
self,
Value::OpenContainer { .. } | Value::CloseContainer { .. }
)
}
pub fn is_string(&self) -> bool {
matches!(self, Value::String)
}
pub fn container_type(&self) -> Option<ContainerType> {
match self {
Value::OpenContainer { container_type, .. } => Some(*container_type),
Value::CloseContainer { container_type, .. } => Some(*container_type),
_ => None,
}
}
pub fn is_opening_of_container(&self) -> bool {
matches!(self, Value::OpenContainer { .. })
}
pub fn is_closing_of_container(&self) -> bool {
matches!(self, Value::CloseContainer { .. })
}
pub fn is_collapsed(&self) -> bool {
match self {
Value::OpenContainer { collapsed, .. } => *collapsed,
Value::CloseContainer { collapsed, .. } => *collapsed,
_ => false,
}
}
pub fn is_expanded(&self) -> bool {
!self.is_collapsed()
}
pub fn is_array(&self) -> bool {
matches!(
self,
Value::OpenContainer {
container_type: ContainerType::Array,
..
} | Value::CloseContainer {
container_type: ContainerType::Array,
..
}
)
}
fn toggle_collapsed(&mut self) {
self.set_collapsed(!self.is_collapsed())
}
fn expand(&mut self) {
self.set_collapsed(false)
}
fn collapse(&mut self) {
self.set_collapsed(true)
}
fn set_collapsed(&mut self, val: bool) {
match self {
Value::OpenContainer {
ref mut collapsed, ..
} => *collapsed = val,
Value::CloseContainer {
ref mut collapsed, ..
} => *collapsed = val,
_ => {}
}
}
fn first_child(&self) -> OptionIndex {
match self {
Value::OpenContainer { first_child, .. } => OptionIndex::Index(*first_child),
_ => OptionIndex::Nil,
}
}
fn last_child(&self) -> OptionIndex {
match self {
Value::CloseContainer { last_child, .. } => OptionIndex::Index(*last_child),
_ => OptionIndex::Nil,
}
}
fn pair_index(&self) -> OptionIndex {
match self {
Value::OpenContainer { close_index, .. } => OptionIndex::Index(*close_index),
Value::CloseContainer { open_index, .. } => OptionIndex::Index(*open_index),
_ => OptionIndex::Nil,
}
}
}
pub fn parse_top_level_json(json: String) -> Result<FlatJson, String> {
let (rows, pretty, depth) = jsonparser::parse(json)?;
Ok(FlatJson(rows, pretty, depth))
}
pub fn parse_top_level_yaml(yaml: String) -> Result<FlatJson, String> {
let (rows, pretty, depth) = yamlparser::parse(yaml)?;
Ok(FlatJson(rows, pretty, depth))
}
#[cfg(test)]
mod tests {
use super::*;
const OBJECT: &str = r#"{
"1": 1,
"2": [
3,
"4"
],
"6": {
"7": null,
"8": true,
"9": 9
},
"11": 11
}"#;
const NESTED_OBJECT: &str = r#"[
{
"2": [
3
],
"5": {
"6": 6
}
}
]"#;
#[test]
fn test_flatten_json() {
let fj = parse_top_level_json(OBJECT.to_owned()).unwrap();
assert_flat_json_fields(
"parent",
&fj,
vec![NIL, 0, 0, 2, 2, 0, 0, 6, 6, 6, 0, 0, NIL],
|elem| elem.parent,
);
assert_flat_json_fields(
"prev_sibling",
&fj,
vec![NIL, NIL, 1, NIL, 3, NIL, 2, NIL, 7, 8, NIL, 6, NIL],
|elem| elem.prev_sibling,
);
assert_flat_json_fields(
"next_sibling",
&fj,
vec![NIL, 2, 6, 4, NIL, NIL, 11, 8, 9, NIL, NIL, NIL, NIL],
|elem| elem.next_sibling,
);
assert_flat_json_fields(
"first_child",
&fj,
vec![1, NIL, 3, NIL, NIL, NIL, 7, NIL, NIL, NIL, NIL, NIL, NIL],
|elem| elem.first_child(),
);
assert_flat_json_fields(
"last_child",
&fj,
vec![NIL, NIL, NIL, NIL, NIL, 4, NIL, NIL, NIL, NIL, 9, NIL, 11],
|elem| elem.last_child(),
);
assert_flat_json_fields(
"{open,close}_index",
&fj,
vec![12, NIL, 5, NIL, NIL, 2, 10, NIL, NIL, NIL, 6, NIL, 0],
|elem| elem.pair_index(),
);
assert_flat_json_fields(
"depth",
&fj,
vec![0, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1, 1, 0],
|elem| OptionIndex::Index(elem.depth),
);
}
fn assert_flat_json_fields<T: Into<OptionIndex> + Debug + Copy>(
field: &'static str,
fj: &FlatJson,
field_values: Vec<T>,
accessor_fn: fn(&Row) -> OptionIndex,
) {
assert_eq!(
fj.0.len(),
field_values.len(),
"length of flat json and field_values don't match",
);
for (i, (elem, expected_value)) in fj.0.iter().zip(field_values.iter()).enumerate() {
assert_eq!(
accessor_fn(elem),
Into::<OptionIndex>::into(*expected_value),
"incorrect {} at index {}",
field,
i,
);
}
}
#[test]
fn test_first_visible_ancestor() {
let mut fj = parse_top_level_json(NESTED_OBJECT.to_owned()).unwrap();
assert_eq!(fj.first_visible_ancestor(3), 3);
assert_eq!(fj.first_visible_ancestor(6), 6);
fj.collapse(5);
assert_eq!(fj.first_visible_ancestor(6), 5);
assert_eq!(fj.first_visible_ancestor(5), 5);
fj.collapse(1);
assert_eq!(fj.first_visible_ancestor(6), 1);
fj.expand(5);
assert_eq!(fj.first_visible_ancestor(6), 1);
fj.collapse(0);
assert_eq!(fj.first_visible_ancestor(6), 0);
}
#[test]
fn test_move_by_visible_rows_simple() {
let fj = parse_top_level_json(OBJECT.to_owned()).unwrap();
assert_visited_rows(&fj, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, NIL]);
}
#[test]
fn test_move_by_visible_rows_collapsed() {
let mut fj = parse_top_level_json(NESTED_OBJECT.to_owned()).unwrap();
fj.collapse(2);
assert_visited_rows(&fj, vec![1, 2, 5, 6, 7, 8, 9, NIL]);
fj.collapse(5);
assert_visited_rows(&fj, vec![1, 2, 5, 8, 9, NIL]);
fj.collapse(1);
assert_visited_rows(&fj, vec![1, 9, NIL]);
fj.collapse(0);
assert_visited_rows(&fj, vec![NIL]);
}
#[test]
fn test_move_by_items_simple() {
let fj = parse_top_level_json(OBJECT.to_owned()).unwrap();
assert_visited_items(&fj, vec![1, 2, 3, 4, 6, 7, 8, 9, 11, NIL]);
let fj = parse_top_level_json(NESTED_OBJECT.to_owned()).unwrap();
assert_visited_items(&fj, vec![1, 2, 3, 5, 6, NIL]);
}
#[test]
fn test_move_by_items_collapsed() {
let mut fj = parse_top_level_json(NESTED_OBJECT.to_owned()).unwrap();
fj.collapse(2);
assert_visited_items(&fj, vec![1, 2, 5, 6, NIL]);
fj.collapse(5);
assert_visited_items(&fj, vec![1, 2, 5, NIL]);
fj.expand(2);
assert_visited_items(&fj, vec![1, 2, 3, 5, NIL]);
fj.collapse(1);
assert_visited_items(&fj, vec![1, NIL]);
fj.collapse(0);
assert_visited_items(&fj, vec![NIL]);
}
fn assert_row_iter(
movement_name: &'static str,
fj: &FlatJson,
start_index: Index,
expected_visited_rows: &Vec<usize>,
movement_fn: fn(&FlatJson, Index) -> OptionIndex,
) {
let mut curr_index = start_index;
for expected_index in expected_visited_rows.iter() {
let next_index = movement_fn(fj, curr_index).to_usize();
assert_eq!(
next_index,
*expected_index,
"expected {}({}) to be {:?}",
movement_name,
curr_index,
OptionIndex::from(*expected_index),
);
curr_index = next_index;
}
}
fn assert_visited_rows(fj: &FlatJson, mut expected: Vec<usize>) {
assert_next_visited_rows(fj, 0, &expected);
let mut start = 0;
if expected.len() > 1 {
start = expected[expected.len() - 2];
expected.pop();
expected.pop();
expected.reverse();
expected.push(0);
expected.push(NIL);
}
assert_prev_visited_rows(fj, start, &expected);
}
fn assert_next_visited_rows(fj: &FlatJson, start_index: Index, expected: &Vec<usize>) {
assert_row_iter(
"next_visible_row",
fj,
start_index,
expected,
FlatJson::next_visible_row,
);
}
fn assert_prev_visited_rows(fj: &FlatJson, start_index: Index, expected: &Vec<usize>) {
assert_row_iter(
"prev_visible_row",
fj,
start_index,
expected,
FlatJson::prev_visible_row,
);
}
fn assert_visited_items(fj: &FlatJson, mut expected: Vec<usize>) {
assert_next_visited_items(fj, 0, &expected);
let mut start = 0;
if expected.len() > 1 {
start = expected[expected.len() - 2];
expected.pop();
expected.pop();
expected.reverse();
expected.push(0);
expected.push(NIL);
}
assert_prev_visited_items(fj, start, &expected);
}
fn assert_next_visited_items(fj: &FlatJson, start_index: Index, expected: &Vec<usize>) {
assert_row_iter("next_item", fj, start_index, expected, FlatJson::next_item);
}
fn assert_prev_visited_items(fj: &FlatJson, start_index: Index, expected: &Vec<usize>) {
assert_row_iter("prev_item", fj, start_index, expected, FlatJson::prev_item);
}
#[test]
fn test_root_object_build_path_to_node() {
use PathType::*;
const ROOT_OBJECT: &str = r#"{
"non js key": 1,
"plain_key": [
{},
{
"nested": 5,
},
],
}"#;
let fj = parse_top_level_json(ROOT_OBJECT.to_owned()).unwrap();
assert!(fj.build_path_to_node(Dot, 0).is_err());
assert!(fj.build_path_to_node(Bracket, 0).is_err());
assert_eq!(".", fj.build_path_to_node(Query, 0).unwrap());
assert_eq!("", fj.build_path_to_node(DotWithTopLevelIndex, 0).unwrap());
let path = r#"["non js key"]"#;
let paths = (path, path, r#".["non js key"]"#, path);
assert_paths_to_node(&fj, 1, paths);
let nested_paths = (
".plain_key[1].nested",
r#"["plain_key"][1]["nested"]"#,
".plain_key[].nested",
".plain_key[1].nested",
);
assert_paths_to_node(&fj, 5, nested_paths);
}
#[test]
fn test_root_array_build_path_to_node() {
use PathType::*;
const ROOT_ARRAY: &str = r#"[
1,
{
"nested": {
"more nested": 4,
},
},
]"#;
let fj = parse_top_level_json(ROOT_ARRAY.to_owned()).unwrap();
assert!(fj.build_path_to_node(Dot, 0).is_err());
assert!(fj.build_path_to_node(Bracket, 0).is_err());
assert_eq!(".", fj.build_path_to_node(Query, 0).unwrap());
assert_eq!("", fj.build_path_to_node(DotWithTopLevelIndex, 0).unwrap());
let paths = ("[0]", "[0]", ".[]", "[0]");
assert_paths_to_node(&fj, 1, paths);
let nested_paths = (
r#"[1].nested["more nested"]"#,
r#"[1]["nested"]["more nested"]"#,
r#".[].nested["more nested"]"#,
r#"[1].nested["more nested"]"#,
);
assert_paths_to_node(&fj, 4, nested_paths);
}
#[test]
fn test_multi_top_level_build_path_to_node() {
use PathType::*;
const MULTI_TOP_LEVEL: &str = r#"[
{
"nested": [
3,
],
},
]
{
"plain_key": [
{
"nested": 10,
},
],
}"#;
let fj = parse_top_level_json(MULTI_TOP_LEVEL.to_owned()).unwrap();
assert!(fj.build_path_to_node(Dot, 0).is_err());
assert!(fj.build_path_to_node(Bracket, 0).is_err());
assert_eq!(".", fj.build_path_to_node(Query, 0).unwrap());
assert_eq!(
"[0]",
fj.build_path_to_node(DotWithTopLevelIndex, 0).unwrap()
);
assert!(fj.build_path_to_node(Dot, 7).is_err());
assert!(fj.build_path_to_node(Bracket, 7).is_err());
assert_eq!(".", fj.build_path_to_node(Query, 7).unwrap());
assert_eq!(
"[1]",
fj.build_path_to_node(DotWithTopLevelIndex, 7).unwrap()
);
let paths = (
"[0].nested[0]",
r#"[0]["nested"][0]"#,
".[].nested[]",
"[0][0].nested[0]",
);
assert_paths_to_node(&fj, 3, paths);
let paths = (
".plain_key[0].nested",
r#"["plain_key"][0]["nested"]"#,
".plain_key[].nested",
"[1].plain_key[0].nested",
);
assert_paths_to_node(&fj, 10, paths);
}
#[test]
fn test_build_path_to_node_yaml_non_string_key() {
use PathType::*;
const YAML: &str = r#"{
[1, 1]: 1,
}"#;
let fj = parse_top_level_yaml(YAML.to_owned()).unwrap();
assert_eq!("[[1, 1]]", fj.build_path_to_node(Dot, 1).unwrap());
assert_eq!("[[1, 1]]", fj.build_path_to_node(Bracket, 1).unwrap());
assert!(fj.build_path_to_node(Query, 1).is_err());
}
#[track_caller]
fn assert_paths_to_node(fj: &FlatJson, index: Index, paths: (&str, &str, &str, &str)) {
use PathType::*;
let dot = fj.build_path_to_node(Dot, index).unwrap();
let bracket = fj.build_path_to_node(Bracket, index).unwrap();
let query = fj.build_path_to_node(Query, index).unwrap();
let dot_top_level = fj.build_path_to_node(DotWithTopLevelIndex, index).unwrap();
assert_eq!(
paths,
(
dot.as_str(),
bracket.as_str(),
query.as_str(),
dot_top_level.as_str()
)
);
}
#[test]
fn test_pretty_print() {
const JSON: &str = r#"{"a":1,"b":[2,{},[],false],"c":null}
[ "d" , [1,{ "e" : 7 }] ]"#;
const PRETTY: &str = r#"{
"a": 1,
"b": [
2,
{},
[],
false
],
"c": null
}
[
"d",
[
1,
{
"e": 7
}
]
]
"#;
let fj = parse_top_level_json(JSON.to_owned()).unwrap();
assert_eq!(PRETTY, fj.pretty_printed().unwrap());
}
#[test]
fn test_pretty_printed_value() {
const JSON: &str = r#"[[{"3":3,"4":[5, 6, {"8": false}]}]]"#;
let fj = parse_top_level_json(JSON.to_owned()).unwrap();
const PRETTY_INNER_OBJ: &str = r#"{
"3": 3,
"4": [
5,
6,
{
"8": false
}
]
}
"#;
assert_eq!(PRETTY_INNER_OBJ, fj.pretty_printed_value(2).unwrap());
assert_eq!("3", fj.pretty_printed_value(3).unwrap());
const PRETTY_ARRAY: &str = r#"[
5,
6,
{
"8": false
}
]
"#;
assert_eq!(PRETTY_ARRAY, fj.pretty_printed_value(4).unwrap());
assert_eq!("6", fj.pretty_printed_value(6).unwrap());
const PRETTY_NESTED_OBJ: &str = "{\n \"8\": false\n}\n";
assert_eq!(PRETTY_NESTED_OBJ, fj.pretty_printed_value(7).unwrap());
}
}