use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::core::{SkillError, Tainted};
use crate::runtime::StepCtx;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Capability(pub String);
impl Capability {
pub fn new(s: impl Into<String>) -> Self {
Self(s.into())
}
}
impl std::fmt::Display for Capability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl From<&str> for Capability {
fn from(s: &str) -> Self {
Self(s.to_owned())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AgentRef {
pub name: String,
pub version: String,
pub digest: crate::core::Digest,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SkillDescriptor {
pub name: String,
pub provides: Vec<Capability>,
}
impl SkillDescriptor {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
provides: Vec::new(),
}
}
#[must_use]
pub fn provides(mut self, cap: impl Into<Capability>) -> Self {
self.provides.push(cap.into());
self
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Outcome {
Done(Tainted<Value>),
Replan { reason: String },
Fail { reason: String },
}
impl Outcome {
pub fn done(v: Tainted<Value>) -> Self {
Self::Done(v)
}
pub fn fail(reason: impl Into<String>) -> Self {
Self::Fail {
reason: reason.into(),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Compensation {
#[default]
Undeclared,
Compensatable,
Pivot,
Unnecessary,
}
#[async_trait]
pub trait Skill: Send + Sync + std::fmt::Debug + 'static {
fn descriptor(&self) -> SkillDescriptor;
async fn invoke(
&self,
cx: &mut StepCtx<'_>,
input: Tainted<Value>,
) -> Result<Outcome, SkillError>;
fn compensation(&self) -> Compensation {
Compensation::Undeclared
}
async fn compensate(
&self,
_cx: &mut StepCtx<'_>,
_output: &Tainted<Value>,
) -> Result<(), SkillError> {
Err(SkillError::Other(
"this step declares Compensation::Compensatable but does not implement \
compensate()"
.into(),
))
}
}