mod copy;
mod delete;
mod errors;
mod parser;
mod query;
mod set;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::str;
use clap::{Parser, Subcommand};
use errors::TomliError;
use toml_edit::DocumentMut;
#[derive(Parser)]
#[command(version)]
struct Cli {
#[arg(short, long, global = true)]
filepath: Option<PathBuf>,
#[arg(short = 'i', long, global = true)]
in_place: bool,
#[arg(short = 'n', long, global = true)]
strip_trailing_newline: bool,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Query {
query: String,
},
Set {
query: String,
value: String,
#[arg(value_enum, short = 't', long = "type", default_value_t = ValueType::Str)]
value_type: ValueType,
#[arg(verbatim_doc_comment, long, default_value_t = false)]
dotted_key: bool,
},
Delete {
#[arg(short = 'e', long)]
if_exists: bool,
query: String,
},
Copy {
source_query: String,
destination: PathBuf,
destination_query: String,
#[arg(verbatim_doc_comment, long, default_value_t = false)]
dotted_key: bool,
},
}
#[derive(clap::ValueEnum, Clone, Debug)]
enum ValueType {
Str,
Int,
Float,
Bool,
Datetime,
}
fn read_input(filepath: Option<&PathBuf>) -> Result<DocumentMut, TomliError> {
let input = if let Some(filepath) = filepath {
std::fs::read_to_string(filepath)?
} else {
std::io::read_to_string(std::io::stdin())?
};
Ok(input.parse::<DocumentMut>()?)
}
fn main() {
let cli = Cli::parse();
let mut document = read_input(cli.filepath.as_ref()).unwrap_or_else(|err| {
eprintln!("{}", err);
std::process::exit(1);
});
let (query, result, can_write, filepath) = match cli.command {
Commands::Copy {
source_query,
destination,
destination_query,
dotted_key,
} => {
let mut destination_document = read_input(Some(&destination)).unwrap_or_default();
(
source_query.clone(),
copy::exec(
&document,
&source_query,
&mut destination_document,
&destination_query,
dotted_key,
),
true,
Some(destination.clone()),
)
}
Commands::Query { query } => (
query.clone(),
query::exec(&document, &query),
false,
cli.filepath,
),
Commands::Set {
query,
value,
value_type,
dotted_key,
} => (
query.clone(),
set::exec(&mut document, &query, &value, value_type, dotted_key),
true,
cli.filepath,
),
Commands::Delete { if_exists, query } => {
let mut result = delete::exec(&mut document, &query);
if if_exists {
result = Ok(document.to_string())
}
(query.clone(), result, true, cli.filepath)
}
};
match result {
Err(error) => {
match error {
TomliError::QuerySyntaxError(position) => eprintln!(
"{}:\n\n{}\n{}--^-",
error,
query,
" ".repeat(position.saturating_sub(2)),
),
_ => eprintln!("{}", error),
};
std::process::exit(1);
}
Ok(result) => {
if can_write
&& cli.in_place
&& let Some(filepath) = filepath
{
let mut file =
File::create(filepath).expect("An error occured when trying to save the file");
file.write_all(result.as_bytes())
.expect("An error occured when trying to save the file");
} else if cli.strip_trailing_newline {
print!("{result}");
} else {
println!("{result}");
}
}
};
}