use super::error::{CliError, CliErrorKind};
pub fn enhance_error(mut error: CliError) -> CliError {
let kind_message = match &error.kind {
CliErrorKind::NotFound(msg) => Some(("not_found", msg.clone())),
CliErrorKind::InvalidFormat(msg) => Some(("invalid_format", msg.clone())),
CliErrorKind::ValidationError(msg) => Some(("validation", msg.clone())),
CliErrorKind::ConfigError(msg) => Some(("config", msg.clone())),
_ => None,
};
if let Some((kind, msg)) = kind_message {
error = match kind {
"not_found" => enhance_not_found_error(error, &msg),
"invalid_format" => enhance_invalid_format_error(error, &msg),
"validation" => enhance_validation_error(error, &msg),
"config" => enhance_config_error(error, &msg),
_ => error,
};
}
error
}
fn enhance_not_found_error(mut error: CliError, resource: &str) -> CliError {
if resource.contains("dataset") || resource.contains("Dataset") {
error = error
.with_suggestion("Initialize a new dataset with: oxirs init <dataset-name>")
.with_suggestion("List available datasets with: ls ~/.oxirs/datasets/")
.with_suggestion("Check the dataset path in your configuration file")
.with_code("E001");
}
else if resource.contains(".ttl")
|| resource.contains(".nt")
|| resource.contains(".rdf")
|| resource.contains(".jsonld")
{
error = error
.with_suggestion("Verify the file path is correct and the file exists")
.with_suggestion("Check file permissions with: ls -l <file-path>")
.with_suggestion("Use absolute paths to avoid ambiguity")
.with_code("E002");
}
else if resource.contains(".sparql") || resource.contains(".rq") {
error = error
.with_suggestion("Verify the query file exists: ls -l <query-file>")
.with_suggestion("Try providing the query inline instead of using --file")
.with_code("E003");
}
else if resource.contains("oxirs.toml") || resource.contains(".toml") {
error = error
.with_suggestion("Initialize configuration with: oxirs config init")
.with_suggestion("Specify a different config path with --config <path>")
.with_suggestion("Check example configurations in docs/examples/")
.with_code("E004");
}
error
}
fn enhance_invalid_format_error(mut error: CliError, msg: &str) -> CliError {
let msg_lower = msg.to_lowercase();
if msg_lower.contains("format")
&& (msg_lower.contains("turtle")
|| msg_lower.contains("ntriples")
|| msg_lower.contains("rdf"))
{
error = error
.with_suggestion(
"Supported RDF formats: turtle, ntriples, nquads, trig, rdfxml, jsonld, n3",
)
.with_suggestion("Use --format <format> to specify the format explicitly")
.with_suggestion("Let oxirs auto-detect the format by omitting --format")
.with_code("E010");
}
else if msg_lower.contains("output") || msg_lower.contains("results") {
error = error
.with_suggestion(
"Supported output formats: table, json, csv, tsv, xml, html, markdown, pdf",
)
.with_suggestion("Use --output <format> to specify the output format")
.with_code("E011");
}
else if msg_lower.contains("sparql") || msg_lower.contains("query") {
error = error
.with_suggestion("Validate your SPARQL syntax with: oxirs qparse <query-file>")
.with_suggestion(
"Check for common mistakes: missing PREFIX declarations, unbalanced braces",
)
.with_suggestion(
"Consult SPARQL 1.1 specification: https://www.w3.org/TR/sparql11-query/",
)
.with_code("E012");
}
error
}
fn enhance_validation_error(mut error: CliError, msg: &str) -> CliError {
let msg_lower = msg.to_lowercase();
if msg_lower.contains("dataset name") || msg_lower.contains("dataset") {
error = error
.with_suggestion(
"Dataset names must be alphanumeric (allow _, - but no dots or slashes)",
)
.with_suggestion("Valid examples: my-dataset, test_db, production2024")
.with_suggestion("Invalid examples: my.dataset, test/db, prod.v1")
.with_code("E020");
}
else if msg_lower.contains("iri") || msg_lower.contains("uri") {
error = error
.with_suggestion(
"IRIs must be valid URIs enclosed in angle brackets: <http://example.org/resource>",
)
.with_suggestion("Use prefixed names for brevity: ex:resource")
.with_suggestion("Validate IRIs with: oxirs iri <uri>")
.with_code("E021");
}
else if msg_lower.contains("complex") || msg_lower.contains("complexity") {
error = error
.with_suggestion("Simplify your query by breaking it into smaller subqueries")
.with_suggestion("Use LIMIT to reduce result set size")
.with_suggestion("Add indexes for frequently queried properties")
.with_suggestion("Profile query execution with: oxirs explain <dataset> <query>")
.with_code("E022");
}
error
}
fn enhance_config_error(mut error: CliError, msg: &str) -> CliError {
let msg_lower = msg.to_lowercase();
if msg_lower.contains("missing") || msg_lower.contains("not found") {
error = error
.with_suggestion("Create a default configuration with: oxirs config init")
.with_suggestion("Copy an example config: cp docs/examples/oxirs.toml .")
.with_code("E030");
}
else if msg_lower.contains("toml") || msg_lower.contains("parse") {
error = error
.with_suggestion("Validate TOML syntax with: oxirs config validate")
.with_suggestion("Common TOML errors: unquoted strings, missing closing brackets")
.with_suggestion("Check TOML syntax: https://toml.io/en/")
.with_code("E031");
}
else if msg_lower.contains("dataset") {
error = error
.with_suggestion("Ensure [datasets.<name>] section exists in oxirs.toml")
.with_suggestion("Required fields: name, location, dataset_type")
.with_suggestion("Example: [datasets.mydb]\\nname = \"mydb\"\\nlocation = \"./data\"")
.with_code("E032");
}
error
}
pub fn enhanced_error_from_message(message: impl Into<String>) -> CliError {
let msg = message.into();
let msg_lower = msg.to_lowercase();
let error = if msg_lower.contains("not found") || msg_lower.contains("no such file") {
CliError::not_found(&msg)
} else if msg_lower.contains("invalid")
|| msg_lower.contains("unsupported")
|| msg_lower.contains("unknown")
{
CliError::invalid_format(&msg)
} else if msg_lower.contains("permission denied") {
CliError::permission_denied(&msg)
} else if msg_lower.contains("syntax") || msg_lower.contains("parse") {
CliError::validation_error(&msg)
} else if msg_lower.contains("config") {
CliError::config_error(&msg)
} else {
CliError::new(CliErrorKind::Other(msg))
};
enhance_error(error)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_enhance_dataset_not_found() {
let error = CliError::not_found("Dataset 'mydb' not found");
let enhanced = enhance_error(error);
assert_eq!(enhanced.suggestions.len(), 3);
assert!(enhanced
.suggestions
.iter()
.any(|s| s.contains("oxirs init")));
assert_eq!(enhanced.code.as_deref(), Some("E001"));
}
#[test]
fn test_enhance_invalid_format() {
let error = CliError::invalid_format("Unsupported RDF format 'xyz'");
let enhanced = enhance_error(error);
assert!(!enhanced.suggestions.is_empty());
assert!(enhanced
.suggestions
.iter()
.any(|s| s.contains("turtle") || s.contains("format")));
}
#[test]
fn test_enhance_validation_error() {
let error = CliError::validation_error("Invalid dataset name 'my.dataset'");
let enhanced = enhance_error(error);
assert!(enhanced.suggestions.len() >= 2);
assert!(enhanced
.suggestions
.iter()
.any(|s| s.contains("alphanumeric")));
assert_eq!(enhanced.code.as_deref(), Some("E020"));
}
#[test]
fn test_enhanced_error_from_message() {
let error = enhanced_error_from_message("File not found: data.ttl");
assert!(matches!(error.kind, CliErrorKind::NotFound(_)));
assert!(!error.suggestions.is_empty());
}
}