Skip to content

Commit

Permalink
Improved JSON Functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
CHRISCARLON committed Sep 19, 2024
1 parent d3928a3 commit 70848f8
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 19 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "nebby"
version = "0.1.2"
version = "0.1.3"
edition = "2021"

[[bin]]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ Christopher Carlon

## Version

0.1.2
0.1.3
133 changes: 124 additions & 9 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,101 @@
use colored::Colorize;
use reqwest::blocking::get;
use serde_json::Value;
use std::error::Error;

pub fn simple_api_get_reqwest(url: &str) -> Result<(), Box<dyn std::error::Error>> {
// Make a GET request using reqwest::blocking::get
#[derive(Debug)]
enum NestingLevel {
Flat, // No nesting, or a very shallow structure
Shallow, // Slightly nested
Moderate, // Moderately nested
Deep, // Quite nested
VeryDeep, // Extremely nested
}

// Function to determine the depth of a JSON structure
fn calculate_nesting_level(json: &Value, current_depth: usize) -> usize {
match json {
Value::Object(map) => map
.values()
.map(|value| calculate_nesting_level(value, current_depth + 1))
.max()
.unwrap_or(current_depth),
Value::Array(arr) => arr
.iter()
.map(|value| calculate_nesting_level(value, current_depth + 1))
.max()
.unwrap_or(current_depth),
_ => current_depth,
}
}

// Function to give a rating based on depth
fn get_nesting_level(depth: usize) -> NestingLevel {
match depth {
0 | 1 => NestingLevel::Flat,
2 => NestingLevel::Shallow,
3 => NestingLevel::Moderate,
4 => NestingLevel::Deep,
_ => NestingLevel::VeryDeep,
}
}

// Function to print the list of colors for each level
fn print_color_list() {
println!("{}", "Color List for Each Nesting Level:".purple().bold());
for i in 0..7 {
let colorized_text = get_color_for_level(i, &format!("Level {}: Color", i));
println!("{}", colorized_text);
}
}

// List of colors for different levels
fn get_color_for_level(level: usize, text: &str) -> colored::ColoredString {
match level % 7 {
0 => text.red(),
1 => text.green(),
2 => text.yellow(),
3 => text.blue(),
4 => text.magenta(),
5 => text.cyan(),
_ => text.white(),
}
}

// Recursive function to print the structure of the JSON with colors
fn print_json_structure(json: &Value, indent: usize) {
let colorized_brace = get_color_for_level(indent / 4, "{");
match json {
Value::Object(map) => {
println!("{}{}", " ".repeat(indent), colorized_brace);
for (key, value) in map {
let colorized_key = get_color_for_level(indent / 4, key);
print!("{}\"{}\": ", " ".repeat(indent + 2), colorized_key);
print_json_structure(value, indent + 4);
}
let colorized_closing_brace = get_color_for_level(indent / 4, "}");
println!("{}{}", " ".repeat(indent), colorized_closing_brace);
}
Value::Array(arr) => {
let colorized_open_bracket = get_color_for_level(indent / 4, "[");
println!("{}{}", " ".repeat(indent), colorized_open_bracket);
for value in arr {
print_json_structure(value, indent + 4);
}
let colorized_closing_bracket = get_color_for_level(indent / 4, "]");
println!("{}{}", " ".repeat(indent), colorized_closing_bracket);
}
_ => {
let colorized_value = get_color_for_level(indent / 4, &json.to_string());
println!("{}{}", " ".repeat(indent), colorized_value);
}
}
}

// Function to call and API endpoint with GET and return JSON data
fn simple_api_get_reqwest(url: &str) -> Result<Value, Box<dyn std::error::Error>> {
let response = get(url)?;

// Check if the request was successful
if response.status().is_success() {
let content_type = response
.headers()
Expand All @@ -17,23 +106,49 @@ pub fn simple_api_get_reqwest(url: &str) -> Result<(), Box<dyn std::error::Error
let json: Value = if content_type.contains("application/json") {
response.json()?
} else {
// If it's not explicitly JSON, try to parse the bytes as JSON
let bytes = response.bytes()?;
serde_json::from_slice(&bytes).unwrap_or_else(|_| {
// If parsing as JSON fails, create a JSON object with the raw data
serde_json::json!({
"raw_data": String::from_utf8_lossy(&bytes).into_owned()
})
})
};

// Print the entire JSON structure
println!("{}", "Received JSON:".green().bold());
println!("{}", serde_json::to_string_pretty(&json)?);

Ok(())
Ok(json)
} else {
// If the request was not successful, return an error
Err(format!("Request failed with status: {}", response.status()).into())
}
}

// Function to process JSON and show levels of nesting
pub fn analyze_json_nesting(url: &str) -> Result<(), Box<dyn Error>> {
// Call the api and fetch data
let json = simple_api_get_reqwest(url)?;

// Print the color list
print_color_list();

// Calculate the nesting level
let depth = calculate_nesting_level(&json, 0);
let nesting_level = get_nesting_level(depth);

// Print the nesting level and depth
println!(
"{} {}",
"Nesting level:".blue().bold(),
format!("{:?}", nesting_level).yellow()
);
println!(
"{} {}",
"Depth:".blue().bold(),
format!("{}", depth).yellow()
);

// Print the structure of the JSON with colors
println!("{}", "JSON Structure:".purple().bold());
print_json_structure(&json, 0);

Ok(())
}
25 changes: 24 additions & 1 deletion src/bytes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use reqwest::blocking::get;
use std::io::Read;

#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum FileType {
PDF,
PNG,
Expand All @@ -10,6 +10,9 @@ pub enum FileType {
ZIP,
XLSX,
DOCX,
XLS,
PARQUET,
CSV,
Unknown,
}

Expand All @@ -26,15 +29,32 @@ pub fn view_bytes(url: &str) -> Result<([u8; 100], FileType), Box<dyn std::error

fn identify_file_type(bytes: &[u8]) -> FileType {
match bytes {
// PDF magic number
[0x25, 0x50, 0x44, 0x46, ..] => FileType::PDF,
// PNG magic number
[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, ..] => FileType::PNG,
// JPEG magic number
[0xFF, 0xD8, 0xFF, ..] => FileType::JPEG,
// GIF magic number
[0x47, 0x49, 0x46, 0x38, ..] => FileType::GIF,
// ZIP magic number (used for DOCX, XLSX, etc.)
[0x50, 0x4B, 0x03, 0x04, rest @ ..] => match rest {
[0x14, 0x00, 0x06, 0x00, ..] => FileType::XLSX,
[0x14, 0x00, 0x08, 0x00, ..] => FileType::DOCX,
_ => FileType::ZIP,
},
// Parquet magic number (first 4 bytes: PAR1)
[0x50, 0x41, 0x52, 0x31, ..] => FileType::PARQUET,
// Microsoft Compound File Binary Format (used for XLS, older DOC, etc.)
[0xD0, 0xCF, 0x11, 0xE0, ..] => FileType::XLS,
// Attempt to detect CSV by checking if the first 100 bytes seem to be comma-separated values
_ if bytes
.iter()
.all(|&b| b.is_ascii_alphanumeric() || b == b',' || b == b'\n' || b == b'\r') =>
{
FileType::CSV
}
// Default case for unknown file types
_ => FileType::Unknown,
}
}
Expand All @@ -48,6 +68,9 @@ pub fn get_file_type_string(file_type: &FileType) -> &'static str {
FileType::ZIP => "ZIP",
FileType::XLSX => "Excel (XLSX)",
FileType::DOCX => "Word (DOCX)",
FileType::XLS => "Excel (XLS)",
FileType::PARQUET => "Parquet",
FileType::CSV => "CSV",
FileType::Unknown => "Unknown",
}
}
6 changes: 3 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod api;
mod bytes;
mod excel;
mod utils;
use api::simple_api_get_reqwest;
use api::analyze_json_nesting;
use bytes::{get_file_type_string, view_bytes};
use clap::{Parser, Subcommand};
use excel::{
Expand All @@ -12,7 +12,7 @@ use excel::{
use utils::create_progress_bar;

#[derive(Parser, Debug)]
#[command(author = "Christopher Carlon", version = "0.1.2", about = "Nebby - quickly review basic information about remote xlsx files and API GET requests", long_about = None)]
#[command(author = "Christopher Carlon", version = "0.1.3", about = "Nebby - quickly review basic information about remote xlsx files and API GET requests", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
Expand Down Expand Up @@ -70,7 +70,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
fn process_json(url: &str) -> Result<(), Box<dyn std::error::Error>> {
validate_url(url)?;
let pb = create_progress_bar("Processing JSON...");
let result = simple_api_get_reqwest(url);
let result = analyze_json_nesting(url);
pb.finish_with_message("JSON Processed");
result
}
Expand Down
4 changes: 2 additions & 2 deletions tests/api_tests.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use nebby::api::simple_api_get_reqwest;
use nebby::api::analyze_json_nesting;

#[test]
fn test_simple_api_get_reqwest() {
let url = "https://api.carbonintensity.org.uk/regional/regionid/1";

// Fetch the file
let result = simple_api_get_reqwest(url);
let result = analyze_json_nesting(url);

// Assert that the fetch was successful
assert!(result.is_ok(), "Failed to fetch the remote file");
Expand Down
Loading

0 comments on commit 70848f8

Please sign in to comment.