use thiserror::Error;
#[derive(Error, Debug)]
pub enum WorkflowError {
#[error("Step '{step}' references unknown binding '{binding}'")]
UnknownBinding {
step: String,
binding: String,
},
#[error("Workflow '{workflow}' requires unregistered tool '{tool}'")]
MissingTool {
workflow: String,
tool: String,
},
#[error("Workflow '{workflow}' requires unregistered resource '{resource}'")]
MissingResource {
workflow: String,
resource: String,
},
#[error("Circular dependency detected in workflow: {cycle}")]
CircularDependency {
cycle: String,
},
#[error("Invalid argument mapping in step '{step}': {reason}")]
InvalidMapping {
step: String,
reason: String,
},
#[error("Invalid URI '{uri}': must start with 'resource://' or 'file://'")]
InvalidUri {
uri: String,
},
#[error("Workflow '{workflow}' is missing required field: {field}")]
MissingField {
workflow: String,
field: &'static str,
},
#[error("Step '{step}' output field '{field}' not found in result")]
OutputFieldNotFound {
step: String,
field: String,
},
#[error(transparent)]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}
impl From<crate::Error> for WorkflowError {
fn from(err: crate::Error) -> Self {
Self::Other(Box::new(err))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unknown_binding_error() {
let err = WorkflowError::UnknownBinding {
step: "step1".to_string(),
binding: "result".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("step1"));
assert!(msg.contains("result"));
assert!(msg.contains("unknown binding"));
}
#[test]
fn test_missing_tool_error() {
let err = WorkflowError::MissingTool {
workflow: "my_workflow".to_string(),
tool: "greet".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("my_workflow"));
assert!(msg.contains("greet"));
assert!(msg.contains("unregistered tool"));
}
#[test]
fn test_missing_resource_error() {
let err = WorkflowError::MissingResource {
workflow: "my_workflow".to_string(),
resource: "resource://test/guide".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("my_workflow"));
assert!(msg.contains("resource://test/guide"));
assert!(msg.contains("unregistered resource"));
}
#[test]
fn test_circular_dependency_error() {
let err = WorkflowError::CircularDependency {
cycle: "step1 -> step2 -> step1".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("Circular dependency"));
assert!(msg.contains("step1 -> step2 -> step1"));
}
#[test]
fn test_invalid_mapping_error() {
let err = WorkflowError::InvalidMapping {
step: "step1".to_string(),
reason: "field not found".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("step1"));
assert!(msg.contains("field not found"));
assert!(msg.contains("Invalid argument mapping"));
}
#[test]
fn test_invalid_uri_error() {
let err = WorkflowError::InvalidUri {
uri: "http://example.com".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("http://example.com"));
assert!(msg.contains("Invalid URI"));
assert!(msg.contains("resource://"));
assert!(msg.contains("file://"));
}
#[test]
fn test_missing_field_error() {
let err = WorkflowError::MissingField {
workflow: "my_workflow".to_string(),
field: "name",
};
let msg = err.to_string();
assert!(msg.contains("my_workflow"));
assert!(msg.contains("name"));
assert!(msg.contains("missing required field"));
}
#[test]
fn test_output_field_not_found_error() {
let err = WorkflowError::OutputFieldNotFound {
step: "step1".to_string(),
field: "result".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("step1"));
assert!(msg.contains("result"));
assert!(msg.contains("not found"));
}
#[test]
fn test_error_conversion_from_crate_error() {
let crate_err = crate::Error::validation("test error");
let workflow_err: WorkflowError = crate_err.into();
match workflow_err {
WorkflowError::Other(_) => {},
_ => panic!("Expected Other variant"),
}
}
#[test]
fn test_error_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<WorkflowError>();
}
#[test]
fn test_error_debug_output() {
let err = WorkflowError::MissingTool {
workflow: "test".to_string(),
tool: "greet".to_string(),
};
let debug = format!("{:?}", err);
assert!(debug.contains("MissingTool"));
}
}