use anyhow::{Error, Result};
use log::{error, info};
use pest::Parser;
use pest_derive::Parser;
use serde_json::{Map, Value};
use std::fs;
use std::path::Path;
use std::str::FromStr;
use thiserror::Error;
#[derive(Parser)]
#[grammar = "json.pest"]
pub struct JSONParser;
#[derive(Error, Debug)]
pub enum ParserError {
#[error("File read error: {0}")]
FileReadError(#[from] std::io::Error),
#[error("JSON parse error")]
JsonParseError,
#[error("Schema validation failed")]
SchemaValidationError,
}
pub fn parse_json(json_str: &str) -> Result<Value, ParserError> {
let pairs = JSONParser::parse(Rule::json, json_str).map_err(|e| {
println!("Parsing error in JSON input: {:?}", e);
ParserError::JsonParseError
})?;
parse_value(pairs)
}
fn parse_value(mut pairs: pest::iterators::Pairs<Rule>) -> Result<Value, ParserError> {
let pair = pairs.next().ok_or_else(|| {
println!("No pairs found in input.");
ParserError::JsonParseError
})?;
match pair.as_rule() {
Rule::json => parse_value(pair.into_inner()),
Rule::object => parse_object(pair),
Rule::array => parse_array(pair),
Rule::string => Ok(Value::String(parse_string(pair)?)),
Rule::number => Ok(Value::Number(parse_number(pair)?)),
Rule::boolean => Ok(Value::Bool(pair.as_str() == "true")),
Rule::null => Ok(Value::Null),
_ => {
println!("Unexpected pair encountered: {:?}", pair.as_rule());
Err(ParserError::JsonParseError)
}
}
}
fn parse_object(pair: pest::iterators::Pair<Rule>) -> Result<Value, ParserError> {
let mut map = Map::new();
for inner_pair in pair.into_inner() {
if inner_pair.as_rule() == Rule::pair {
let mut inner_rules = inner_pair.into_inner();
let key = parse_string(inner_rules.next().ok_or(ParserError::JsonParseError)?)?;
let value = parse_value(inner_rules)?;
map.insert(key, value);
}
}
Ok(Value::Object(map))
}
fn parse_array(pair: pest::iterators::Pair<Rule>) -> Result<Value, ParserError> {
let mut array = Vec::new();
for inner_pair in pair.into_inner() {
let value = parse_value(inner_pair.into_inner())?;
array.push(value);
}
Ok(Value::Array(array))
}
fn parse_string(pair: pest::iterators::Pair<Rule>) -> Result<String, ParserError> {
let mut result = String::new();
for inner_pair in pair.into_inner() {
match inner_pair.as_rule() {
Rule::character => result.push_str(inner_pair.as_str()),
Rule::escape_sequence => {
let escaped = match inner_pair.as_str() {
"\\\"" => "\"",
"\\\\" => "\\",
"\\/" => "/",
"\\b" => "\u{0008}",
"\\f" => "\u{000C}",
"\\n" => "\n",
"\\r" => "\r",
"\\t" => "\t",
escape if escape.starts_with("\\u") => {
let hex = &escape[2..];
let code_point = u32::from_str_radix(hex, 16)
.map_err(|_| ParserError::JsonParseError)?;
let unicode_char = std::char::from_u32(code_point)
.ok_or(ParserError::JsonParseError)?
.to_string();
result.push_str(&unicode_char);
continue;
}
_ => return Err(ParserError::JsonParseError),
};
result.push_str(escaped);
}
_ => {}
}
}
Ok(result)
}
fn parse_number(pair: pest::iterators::Pair<Rule>) -> Result<serde_json::Number, ParserError> {
let number_str = pair.as_str();
serde_json::Number::from_str(number_str).map_err(|_| ParserError::JsonParseError)
}
pub fn validate_json_schema(json: &Value, schema: &Value) -> Result<(), ParserError> {
if json.is_object() && schema.is_object() {
if json
.as_object()
.unwrap()
.keys()
.all(|key| schema.as_object().unwrap().contains_key(key))
{
Ok(())
} else {
Err(ParserError::SchemaValidationError)
}
} else {
Err(ParserError::SchemaValidationError)
}
}
pub fn parse_partial_json(json: &Value, key: &str) -> Option<Value> {
json.get(key).cloned()
}
pub fn edit_json(json: &mut Value, key: &str, new_value: Value) -> Result<(), Error> {
if let Some(obj) = json.as_object_mut() {
obj.insert(key.to_string(), new_value);
Ok(())
} else {
Err(Error::msg("Invalid JSON structure for editing"))
}
}
pub fn convert_to_format(json: &Value, format: &str) -> Result<String, Error> {
match format {
"yaml" => serde_yaml::to_string(json).map_err(|e| Error::msg(e.to_string())),
"xml" => convert_json_to_xml(json),
_ => Err(Error::msg("Unsupported format")),
}
}
fn convert_json_to_xml(json: &Value) -> Result<String, Error> {
let mut writer = Vec::new();
write_xml(json, &mut writer, "root")?;
String::from_utf8(writer).map_err(|e| Error::msg(e.to_string()))
}
fn write_xml<W: std::io::Write>(json: &Value, writer: &mut W, tag_name: &str) -> Result<(), Error> {
match json {
Value::Object(map) => {
writeln!(writer, "<{}>", tag_name)?;
for (key, value) in map {
write_xml(value, writer, key)?;
}
writeln!(writer, "</{}>", tag_name)?;
}
Value::Array(arr) => {
for value in arr {
write_xml(value, writer, tag_name)?;
}
}
Value::String(s) => {
writeln!(writer, "<{0}>{1}</{0}>", tag_name, s)?;
}
Value::Number(num) => {
writeln!(writer, "<{0}>{1}</{0}>", tag_name, num)?;
}
Value::Bool(b) => {
writeln!(writer, "<{0}>{1}</{0}>", tag_name, b)?;
}
Value::Null => {
writeln!(writer, "<{} />", tag_name)?;
}
}
Ok(())
}
pub fn handle_large_json(file_path: &Path) -> Result<(), ParserError> {
let file = fs::File::open(file_path)?;
let stream = serde_json::Deserializer::from_reader(file).into_iter::<Value>();
for value in stream {
match value {
Ok(json_value) => info!("Parsed chunk: {:?}", json_value),
Err(e) => error!("Error parsing chunk: {:?}", e),
}
}
Ok(())
}
pub fn search_by_value(json: &Value, target_value: &str) -> Vec<String> {
let mut results = Vec::new();
search_recursive(json, target_value, &mut results, "".to_string());
results
}
fn search_recursive(json: &Value, target_value: &str, results: &mut Vec<String>, path: String) {
match json {
Value::Object(map) => {
for (key, value) in map {
let new_path = format!("{}.{}", path, key)
.trim_start_matches('.')
.to_string();
if value.is_string() && value.as_str().unwrap() == target_value {
results.push(new_path.clone());
}
search_recursive(value, target_value, results, new_path);
}
}
Value::Array(arr) => {
for (index, item) in arr.iter().enumerate() {
let new_path = format!("{}[{}]", path, index);
search_recursive(item, target_value, results, new_path);
}
}
_ => {}
}
}
pub fn get_by_path(json: &Value, json_path: &str) -> Option<Value> {
let mut current = json;
let parts = json_path.split('.');
for part in parts {
if part.contains('[') && part.contains(']') {
let name = &part[..part.find('[').unwrap()];
let index: usize = part[part.find('[').unwrap() + 1..part.find(']').unwrap()]
.parse()
.ok()?;
current = current.get(name)?.get(index)?;
} else {
current = current.get(part)?;
}
}
Some(current.clone())
}
pub fn minify_json(json: &Value) -> String {
json.to_string()
}
pub fn display_structure(json: &Value) {
display_structure_recursive(json, 0);
}
fn display_structure_recursive(json: &Value, indent: usize) {
match json {
Value::Object(map) => {
for (key, value) in map {
println!("{:indent$}{}", "", key, indent = indent);
display_structure_recursive(value, indent + 2);
}
}
Value::Array(arr) => {
for (index, item) in arr.iter().enumerate() {
println!("{:indent$}[{}]", "", index, indent = indent);
display_structure_recursive(item, indent + 2);
}
}
_ => {}
}
}