use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SchemaVersion {
pub provider: String,
pub format: FormatType,
pub version: u32,
pub label: String,
}
impl SchemaVersion {
pub fn new(provider: &str, format: FormatType, version: u32, label: &str) -> Self {
Self {
provider: provider.to_string(),
format,
version,
label: label.to_string(),
}
}
pub fn id(&self) -> String {
format!(
"{}-{}-v{}",
self.provider,
self.format.as_str(),
self.version
)
}
}
impl std::fmt::Display for SchemaVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.id())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum FormatType {
Json,
Jsonl,
Sqlite,
Markdown,
Binary,
OpenAiApi,
}
impl FormatType {
pub fn as_str(&self) -> &'static str {
match self {
Self::Json => "json",
Self::Jsonl => "jsonl",
Self::Sqlite => "sqlite",
Self::Markdown => "markdown",
Self::Binary => "binary",
Self::OpenAiApi => "openai-api",
}
}
}
impl std::fmt::Display for FormatType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum StorageType {
FilePerSession,
SqliteDb,
SqliteKeyValue,
CloudApi,
Hybrid,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageLocation {
pub description: String,
pub path_pattern: String,
#[serde(default)]
pub platform_paths: HashMap<String, String>,
pub storage_type: StorageType,
#[serde(default)]
pub file_extensions: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderSchema {
pub version: SchemaVersion,
pub extension_version_min: Option<String>,
pub extension_version_max: Option<String>,
pub host_version_min: Option<String>,
pub introduced: Option<String>,
pub deprecated: Option<String>,
pub storage: StorageLocation,
pub session_schema: SessionFormatSchema,
#[serde(default)]
pub db_keys: Vec<DbKeySchema>,
#[serde(default)]
pub notes: Vec<String>,
#[serde(default)]
pub breaking_changes: Vec<String>,
#[serde(default)]
pub tags: Vec<String>,
}
impl ProviderSchema {
pub fn field_count(&self) -> usize {
self.session_schema.fields.len()
}
pub fn id(&self) -> String {
self.version.id()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionFormatSchema {
pub description: String,
pub format: FormatType,
pub fields: Vec<FieldSchema>,
#[serde(default)]
pub nested_objects: HashMap<String, Vec<FieldSchema>>,
#[serde(default)]
pub example: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldSchema {
pub name: String,
#[serde(default)]
pub serialized_name: Option<String>,
pub data_type: DataType,
#[serde(default)]
pub required: bool,
#[serde(default)]
pub default_value: Option<serde_json::Value>,
pub description: String,
#[serde(default)]
pub constraints: Vec<FieldConstraint>,
#[serde(default)]
pub semantic_tag: Option<String>,
#[serde(default)]
pub since_version: Option<String>,
#[serde(default)]
pub removed_in: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum DataType {
String,
Integer,
Float,
Boolean,
Timestamp,
Uuid,
Json,
Array(Box<DataType>),
Object(std::string::String),
Enum(Vec<std::string::String>),
Uri,
Base64,
Optional(Box<DataType>),
}
impl std::fmt::Display for DataType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::String => write!(f, "string"),
Self::Integer => write!(f, "integer"),
Self::Float => write!(f, "float"),
Self::Boolean => write!(f, "boolean"),
Self::Timestamp => write!(f, "timestamp"),
Self::Uuid => write!(f, "uuid"),
Self::Json => write!(f, "json"),
Self::Array(inner) => write!(f, "array<{}>", inner),
Self::Object(name) => write!(f, "object<{}>", name),
Self::Enum(variants) => write!(f, "enum({})", variants.join("|")),
Self::Uri => write!(f, "uri"),
Self::Base64 => write!(f, "base64"),
Self::Optional(inner) => write!(f, "optional<{}>", inner),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum FieldConstraint {
#[serde(rename = "min")]
Min { value: serde_json::Value },
#[serde(rename = "max")]
Max { value: serde_json::Value },
#[serde(rename = "enum")]
Enum { values: Vec<serde_json::Value> },
#[serde(rename = "pattern")]
Pattern { pattern: String },
#[serde(rename = "foreign_key")]
ForeignKey { entity: String, field: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DbKeySchema {
pub key: String,
pub description: String,
pub value_type: DataType,
#[serde(default)]
pub value_fields: Vec<FieldSchema>,
#[serde(default)]
pub required: bool,
#[serde(default)]
pub since_version: Option<String>,
#[serde(default)]
pub removed_in: Option<String>,
#[serde(default)]
pub renamed_to: Option<String>,
}