use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::files_model::UploadFileData;
use crate::shared::{ProviderMetadata, ProviderOptions, ProviderReference, Warning};
#[async_trait]
pub trait SkillsModel: Send + Sync + std::fmt::Debug {
fn provider(&self) -> &str;
fn specification_version(&self) -> &'static str {
"v4"
}
async fn upload_skill(&self, options: UploadSkillOptions) -> Result<UploadSkillResult>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkillFile {
pub path: String,
pub data: UploadFileData,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UploadSkillOptions {
pub files: Vec<SkillFile>,
#[serde(
default,
rename = "displayTitle",
skip_serializing_if = "Option::is_none"
)]
pub display_title: Option<String>,
#[serde(
default,
rename = "providerOptions",
skip_serializing_if = "Option::is_none"
)]
pub provider_options: Option<ProviderOptions>,
}
#[derive(Debug, Clone)]
pub struct UploadSkillResult {
pub provider_reference: ProviderReference,
pub display_title: Option<String>,
pub name: Option<String>,
pub description: Option<String>,
pub latest_version: Option<String>,
pub provider_metadata: Option<ProviderMetadata>,
pub warnings: Vec<Warning>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shared::FileBytes;
#[test]
fn options_serde_roundtrip() {
let opts = UploadSkillOptions {
files: vec![SkillFile {
path: "main.py".into(),
data: UploadFileData::Text {
text: "print(1)".into(),
},
}],
display_title: Some("greeter".into()),
provider_options: None,
};
let json = serde_json::to_value(&opts).unwrap();
assert_eq!(json["displayTitle"], "greeter");
assert_eq!(json["files"][0]["path"], "main.py");
assert_eq!(json["files"][0]["data"]["type"], "text");
}
#[test]
fn skill_file_supports_bytes() {
let f = SkillFile {
path: "asset.bin".into(),
data: UploadFileData::Data {
data: FileBytes::Bytes(vec![1, 2, 3]),
},
};
let v = serde_json::to_value(&f).unwrap();
assert_eq!(v["data"]["type"], "data");
}
}