use crate::{config::Configs, error_reporting::report_serde_json_err}; mod cli { use clap::{Parser, Subcommand}; #[derive(Parser)] struct Args { #[command(subcommand)] command: Command, } #[derive(Clone, Subcommand)] enum Command {} } mod config { use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct Configs<'a> { example_value: i32, example_string: &'a str, } impl Configs<'_> { pub fn read_json(config_text: &str) -> serde_json::Result> { serde_json::from_str(config_text) } } } mod error_reporting { use std::{ops::Range, process}; use ariadne::{Report, Source}; pub fn report_serde_json_err(src: &str, err: serde_json::Error) -> ! { use ariadne::{Color, ColorGenerator, Fmt, Label, Report, ReportKind, Source}; let offset = try_reconstruct_loc(src, err.line(), err.column()); Report::build(ariadne::ReportKind::Error, "test", offset) .with_label( Label::new(("test", offset..offset)).with_message("Something went wrong here!"), ) .with_message(err.to_string()) .with_note( "We'd like to give better errors, but serde errors are horrible to work with...", ) .finish() .print(("test", Source::from(src))) .unwrap(); process::exit(1); } fn try_reconstruct_loc(src: &str, line_nr: usize, col_nr: usize) -> usize { let (line_nr, col_nr) = (line_nr - 1, col_nr - 1); src.lines().enumerate().fold(0, |acc, (i, line)| { if i < line_nr { acc + line.len() } else if i == line_nr { acc + col_nr } else { acc } }) } } fn main() { const TEST_JSON_CONFIG: &str = "{ \"example_value\": 42, \"example_string\": \"meow\" }"; const TEST_JSON_CONFIG_BROKEN: &str = "{ \"example_value\": \"42\", \"example_string\": \"meow\" }"; dbg!(Configs::read_json(TEST_JSON_CONFIG_BROKEN) .map_err(|e| report_serde_json_err(TEST_JSON_CONFIG_BROKEN, e))); }