use clap::Args as ClapArgs;
use influxdb3_plugin_schemas::Index;
use influxdb3_plugin_sdk::{SdkError, ValidationFailure, validate};
use std::io::Write;
use std::path::PathBuf;
use crate::cli_error::CliError;
use crate::color::Stream;
use crate::output::error_mapping::{ErrorContext, json_error_from_sdk, json_error_from_validation};
use crate::output::json::{JsonError, ValidateResult, write_envelope_ok};
use crate::output::{Env, OutputMode, RealEnv, resolve_output_mode};
use crate::path_display::absolutize_for_json;
use crate::style::Palette;
#[derive(Debug, ClapArgs)]
pub(crate) struct Args {
#[arg(default_value = ".")]
plugin_dir: PathBuf,
#[arg(long, value_enum)]
output: Option<OutputMode>,
#[arg(long)]
index: Option<PathBuf>,
}
impl Args {
pub(crate) fn run(self) -> anyhow::Result<()> {
run_with_env(self, &RealEnv)
}
}
fn run_with_env(args: Args, env: &dyn Env) -> anyhow::Result<()> {
let mode = resolve_output_mode(args.output, env);
let stdout_palette = Palette::for_stream(Stream::Stdout, mode, env, env.stdout_is_terminal());
let parsed_index = match &args.index {
Some(index_path) => match std::fs::read_to_string(index_path) {
Ok(raw) => match Index::parse_json(&raw) {
Ok(index) => Some(index),
Err(schema_errors) => {
return Err(CliError::runtime(json_error_from_sdk(
&SdkError::from(schema_errors),
ErrorContext::Validate,
)));
}
},
Err(io_err) => {
let index_display = absolutize_for_json(index_path)?.display().to_string();
let diag = JsonError {
code: "validate::index_read_failed".into(),
message: format!("failed to read --index {index_display}: {io_err}"),
field: Some(index_display.clone()),
details: Some(serde_json::json!({
"path": index_display,
"io_message": io_err.to_string(),
})),
diagnostics: vec![],
cause: vec![io_err.to_string()],
};
let je = JsonError {
code: "validate::failed".into(),
message: "1 validation diagnostic(s)".into(),
field: None,
details: None,
diagnostics: vec![diag],
cause: vec![],
};
return Err(CliError::runtime(je));
}
},
None => None,
};
let result = run_validation(&args.plugin_dir, parsed_index.as_ref());
match (mode, result) {
(OutputMode::Json, Ok(())) => {
write_envelope_ok(&mut std::io::stdout(), ValidateResult {})?;
Ok(())
}
(OutputMode::Human, Ok(())) => {
let ok = stdout_palette.success.render();
let ok_reset = stdout_palette.success.render_reset();
writeln!(
std::io::stdout(),
"{ok}validation passed: 0 diagnostics{ok_reset}"
)?;
Ok(())
}
(_, Err(ValidationFailure::Invalid(errs))) => {
let je = JsonError {
code: "validate::failed".into(),
message: format!("{} validation diagnostic(s)", errs.len()),
field: None,
details: None,
diagnostics: errs.iter().map(json_error_from_validation).collect(),
cause: vec![],
};
Err(CliError::runtime(je))
}
(_, Err(ValidationFailure::Io { source, path })) => {
let je = json_error_from_sdk(&SdkError::Io { source, path }, ErrorContext::Validate);
Err(CliError::runtime(je))
}
(_, Err(e @ ValidationFailure::InvalidExcludePattern { .. })) => {
let je = json_error_from_sdk(&SdkError::from(e), ErrorContext::Validate);
Err(CliError::runtime(je))
}
(_, Err(other)) => {
let je = JsonError {
code: "validate::failed".into(),
message: other.to_string(),
field: None,
details: None,
diagnostics: vec![],
cause: vec![],
};
Err(CliError::runtime(je))
}
}
}
fn run_validation(
plugin_dir: &std::path::Path,
index: Option<&Index>,
) -> Result<(), ValidationFailure> {
let result = match index {
Some(idx) => validate::plugin_dir_with_index(plugin_dir, idx),
None => validate::plugin_dir(plugin_dir),
};
result.map(|_validated| ())
}