use crate::context::IterationContext;
use crate::factories::ActionFactoryError;
use crate::util::rand_action_name;
use crate::{ActionFactory, FatigueTesterRunInformation};
use reqwest::StatusCode;
use serde_yaml::Value;
use std::sync::Arc;
use std::time::Duration;
use url::Url;
pub mod delay;
pub mod print;
pub mod request;
pub fn register_default_actions(factory: &mut ActionFactory) -> Result<(), ActionFactoryError> {
factory.register_builder(request::new_builder())?;
factory.register_builder(delay::new_builder())?;
factory.register_builder(print::new_builder())?;
Ok(())
}
pub type ActionResult = Result<ActionExecutionInfo, ActionError>;
#[derive(Debug)]
pub enum ActionExecutionInfo {
Ok,
HttpCall {
timing: Duration,
status_code: StatusCode,
url: Url,
},
Multi {
items: Vec<ActionExecutionInfo>,
},
}
#[async_trait]
pub trait Action {
fn suggest_default_name(&self) -> Option<String>;
async fn execute(&self, context: &mut IterationContext) -> ActionResult;
}
pub(crate) struct InternalAction {
internal: ActionPointer,
name: Arc<String>,
}
impl InternalAction {
pub fn new(internal: ActionPointer, name: Option<String>) -> Self {
let name = match name {
Some(n) => n,
None => internal
.suggest_default_name()
.unwrap_or_else(rand_action_name),
};
let name = Arc::new(name);
InternalAction { internal, name }
}
pub async fn execute(&self, context: &mut IterationContext) -> InternalActionResult {
let internal = self.internal.execute(context).await;
let name = self.name.clone();
InternalActionResult { internal, name }
}
}
#[derive(Debug)]
pub(crate) struct InternalActionResult {
pub internal: ActionResult,
pub name: Arc<String>,
}
pub type ActionPointer = Box<dyn Action + Send + Sync>;
#[derive(Debug, Error)]
pub enum ActionError {
#[error(transparent)]
Request(#[from] reqwest::Error),
#[error(transparent)]
Templating(#[from] liquid::Error),
#[error(transparent)]
Url(#[from] url::ParseError),
#[error(transparent)]
Yaml(#[from] serde_yaml::Error),
}
pub trait ActionBuilder: Send + Sync {
fn get_type_name(&self) -> &'static str;
fn build(
&self,
properties: &Value,
run_info: &FatigueTesterRunInformation,
) -> Result<ActionPointer, ActionBuilderError>;
}
pub fn action_validation_error<S: Into<String>>(
builder: &impl ActionBuilder,
msg: S,
) -> Result<ActionPointer, ActionBuilderError> {
Err(ActionBuilderError::ValidationError(
builder.get_type_name(),
msg.into(),
))
}
#[derive(Debug, Error)]
pub enum ActionBuilderError {
#[error("error deserializing properties: `{0}`")]
YamlDeserializationError(#[from] serde_yaml::Error),
#[error("error building action `{0}`: `{1}`")]
ValidationError(&'static str, String),
#[error("error parsing url: `{0}`")]
UrlParse(#[from] url::ParseError),
#[error(transparent)]
Request(#[from] reqwest::Error),
#[error(transparent)]
Templating(#[from] liquid::Error),
}