use thiserror::Error;
#[derive(Debug, Error)]
pub enum VikaError {
#[error("Schema error: {0}")]
Schema(#[from] SchemaError),
#[error("Config error: {0}")]
Config(#[from] ConfigError),
#[error("Network error: {0}")]
Network(#[from] NetworkError),
#[error("File system error: {0}")]
FileSystem(#[from] FileSystemError),
#[error("Generation error: {0}")]
Generation(#[from] GenerationError),
#[error("Validation error: {0}")]
Validation(#[from] ValidationError),
}
#[derive(Debug, Error)]
pub enum SchemaError {
#[error("Schema not found: {name}")]
NotFound { name: String },
#[error("Invalid schema reference: {ref_path}")]
InvalidReference { ref_path: String },
#[error("Circular reference detected: {cycle:?}")]
CircularReference { cycle: Vec<String> },
#[error("Unsupported schema type: {schema_type}")]
UnsupportedType { schema_type: String },
#[error("Parameter not found: {name}")]
ParameterNotFound { name: String },
#[error("Request body not found: {name}")]
RequestBodyNotFound { name: String },
#[error("Response not found: {name}")]
ResponseNotFound { name: String },
#[error("Unsupported reference path: {ref_path}")]
UnsupportedReferencePath { ref_path: String },
}
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("Config file not found: {path}")]
NotFound { path: String },
#[error("Invalid config: {message}")]
Invalid { message: String },
#[error("Failed to read config: {0}")]
ReadError(#[from] std::io::Error),
#[error("Failed to parse config: {0}")]
ParseError(#[from] serde_json::Error),
#[error("Required field missing: {field}")]
MissingField { field: String },
#[error("Invalid output directory: {path}")]
InvalidOutputDirectory { path: String },
#[error("No specs are defined. At least one spec must be configured in the 'specs' array.")]
NoSpecDefined,
#[error("Duplicate spec name found: {name}")]
DuplicateSpecName { name: String },
#[error("Invalid spec name: {name}. Spec names must be non-empty and contain only alphanumeric characters, hyphens, and underscores.")]
InvalidSpecName { name: String },
}
#[derive(Debug, Error)]
pub enum NetworkError {
#[error("Failed to fetch spec from {url}: {source}")]
FetchFailed {
url: String,
#[source]
source: reqwest::Error,
},
#[error("Failed to read response from {url}: {source}")]
ReadResponseFailed {
url: String,
#[source]
source: reqwest::Error,
},
#[error("Invalid URL: {url}")]
InvalidUrl { url: String },
}
#[derive(Debug, Error)]
pub enum FileSystemError {
#[error("Failed to create directory: {path}")]
CreateDirectoryFailed {
path: String,
#[source]
source: std::io::Error,
},
#[error("Failed to read file: {path}")]
ReadFileFailed {
path: String,
#[source]
source: std::io::Error,
},
#[error("Failed to write file: {path}")]
WriteFileFailed {
path: String,
#[source]
source: std::io::Error,
},
#[error("File not found: {path}")]
FileNotFound { path: String },
#[error("Directory not found: {path}")]
DirectoryNotFound { path: String },
#[error("File was modified by user: {path}\n This file was previously generated by vika-cli but has been modified.\n Use --force to overwrite, or --backup to create a backup first.")]
FileModifiedByUser { path: String },
}
#[derive(Debug, Error)]
pub enum GenerationError {
#[error("No modules available after filtering")]
NoModulesAvailable,
#[error("Cannot use more than one hook generator at once. Please specify only one of --react-query or --swr")]
InvalidHookFlags,
#[error("No modules selected")]
NoModulesSelected,
#[error("Spec path or URL is required. Use --spec <path-or-url>")]
SpecPathRequired,
#[error("Spec not found: {name}. Available specs: {available:?}")]
SpecNotFound {
name: String,
available: Vec<String>,
},
#[error("Failed to generate TypeScript types: {0}")]
TypeScriptGenerationFailed(String),
#[error("Failed to generate Zod schemas: {0}")]
ZodGenerationFailed(String),
#[error("Failed to generate API client: {0}")]
ApiClientGenerationFailed(String),
#[error("Invalid operation: {message}")]
InvalidOperation { message: String },
#[error("Template error: {0}")]
Template(#[from] TemplateError),
}
#[derive(Debug, Error)]
pub enum ValidationError {
#[error("Invalid TypeScript identifier: {name}")]
InvalidTypeScriptIdentifier { name: String },
#[error("Invalid module name: {name}")]
InvalidModuleName { name: String },
#[error("Validation failed: {message}")]
Failed { message: String },
}
#[derive(Debug, Error)]
pub enum TemplateError {
#[error("Template not found: {name}")]
NotFound { name: String },
#[error("Failed to render template {name}: {message}")]
RenderFailed { name: String, message: String },
#[error("Invalid template syntax in {name}: {message}")]
InvalidSyntax { name: String, message: String },
#[error("Missing required context field {field} in template {name}")]
MissingContextField { name: String, field: String },
#[error("Failed to load template {name}: {source}")]
LoadFailed {
name: String,
#[source]
source: FileSystemError,
},
}
pub type Result<T> = std::result::Result<T, VikaError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_schema_error_display() {
let error = SchemaError::NotFound {
name: "User".to_string(),
};
let error_msg = error.to_string();
assert!(error_msg.contains("User"));
assert!(error_msg.contains("not found"));
}
#[test]
fn test_config_error_display() {
let error = ConfigError::Invalid {
message: "Test error".to_string(),
};
let error_msg = error.to_string();
assert!(error_msg.contains("Test error"));
}
#[test]
fn test_network_error_display() {
let error = NetworkError::InvalidUrl {
url: "invalid-url".to_string(),
};
let error_msg = error.to_string();
assert!(error_msg.contains("invalid-url"));
}
#[test]
fn test_file_system_error_display() {
let error = FileSystemError::FileNotFound {
path: "/test/path".to_string(),
};
let error_msg = error.to_string();
assert!(error_msg.contains("/test/path"));
}
#[test]
fn test_generation_error_display() {
let error = GenerationError::NoModulesAvailable;
let error_msg = error.to_string();
assert!(error_msg.contains("No modules available"));
}
#[test]
fn test_error_conversion() {
let schema_error = SchemaError::NotFound {
name: "Test".to_string(),
};
let vika_error: VikaError = schema_error.into();
let error_msg = vika_error.to_string();
assert!(error_msg.contains("Schema error"));
}
#[test]
fn test_error_context_preservation() {
let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
let config_error = ConfigError::ReadError(io_error);
let vika_error: VikaError = config_error.into();
let error_msg = vika_error.to_string();
assert!(error_msg.contains("Config error"));
}
}