use aion_awl::{Document, TypeBody, TypeRef, builtins::BUILTIN_TYPES, doc_text};
use serde::Serialize;
#[derive(Debug, Serialize)]
pub struct StudioProjection {
pub builtins: &'static [&'static str],
pub types: Vec<StudioType>,
pub workers: Vec<StudioWorker>,
}
#[derive(Debug, Serialize)]
pub struct StudioType {
pub name: String,
pub kind: StudioTypeKind,
pub documentation: Option<String>,
pub fields: Vec<StudioField>,
pub variants: Vec<StudioVariant>,
}
#[derive(Debug, Serialize)]
pub struct StudioVariant {
pub name: String,
pub documentation: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum StudioTypeKind {
Record,
Enum,
Schema,
}
#[derive(Debug, Serialize)]
pub struct StudioField {
pub name: String,
#[serde(rename = "type")]
pub ty: String,
pub documentation: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct StudioWorker {
pub name: String,
pub documentation: Option<String>,
pub actions: Vec<StudioAction>,
}
#[derive(Debug, Serialize)]
pub struct StudioAction {
pub name: String,
pub documentation: Option<String>,
pub params: Vec<StudioField>,
pub return_type: String,
}
pub(super) fn build(document: &Document) -> StudioProjection {
StudioProjection {
builtins: &BUILTIN_TYPES,
types: document
.types
.iter()
.map(|declaration| {
let (kind, fields, variants) = match &declaration.body {
TypeBody::Record { fields } => (
StudioTypeKind::Record,
fields
.iter()
.map(|field| StudioField {
name: field.name.clone(),
ty: render_type(&field.ty),
documentation: prose(&field.docs),
})
.collect(),
Vec::new(),
),
TypeBody::Enum { variants } => (
StudioTypeKind::Enum,
Vec::new(),
variants
.iter()
.map(|variant| StudioVariant {
name: variant.name.clone(),
documentation: prose(&variant.docs),
})
.collect(),
),
TypeBody::SchemaInline { .. } | TypeBody::SchemaImport { .. } => {
(StudioTypeKind::Schema, Vec::new(), Vec::new())
}
};
StudioType {
name: declaration.name.clone(),
kind,
documentation: prose(&declaration.docs),
fields,
variants,
}
})
.collect(),
workers: document
.workers
.iter()
.map(|worker| StudioWorker {
name: worker.name.clone(),
documentation: prose(&worker.docs),
actions: worker
.actions
.iter()
.map(|action| StudioAction {
name: action.name.clone(),
documentation: prose(&action.docs),
params: action
.params
.iter()
.map(|parameter| StudioField {
name: parameter.name.clone(),
ty: render_type(¶meter.ty),
documentation: prose(¶meter.docs),
})
.collect(),
return_type: render_type(&action.returns),
})
.collect(),
})
.collect(),
}
}
fn prose(docs: &[aion_awl::DocLine]) -> Option<String> {
let text = doc_text(docs);
(!text.is_empty()).then_some(text)
}
fn render_type(ty: &TypeRef) -> String {
match ty {
TypeRef::Named { name, .. } => name.clone(),
TypeRef::List { inner, .. } => format!("[{}]", render_type(inner)),
TypeRef::Optional { inner, .. } => format!("{}?", render_type(inner)),
}
}
#[cfg(test)]
#[path = "studio_projection_tests.rs"]
mod studio_projection_tests;