use std::path::PathBuf;
use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ClientProtocol {
#[default]
Rest,
Connect,
}
impl ClientProtocol {
pub(crate) fn subdir(self) -> &'static str {
match self {
ClientProtocol::Rest => "rest",
ClientProtocol::Connect => "connect",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ClientProtocols {
pub rest: bool,
pub connect: bool,
}
impl Default for ClientProtocols {
fn default() -> Self {
Self {
rest: true,
connect: false,
}
}
}
impl ClientProtocols {
pub(crate) fn is_multi(self) -> bool {
self.rest && self.connect
}
pub(crate) fn selected(self) -> Vec<ClientProtocol> {
let mut v = Vec::new();
if self.rest {
v.push(ClientProtocol::Rest);
}
if self.connect {
v.push(ClientProtocol::Connect);
}
v
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Runtime {
#[default]
Prost,
Buffa,
}
#[derive(Debug, Clone)]
pub struct ModelsPath {
template: String,
}
impl ModelsPath {
pub fn new(template: &str) -> Result<Self> {
let test = template
.replace("{service}", "test")
.replace("{version}", "v1");
syn::parse_str::<syn::Path>(&test).map_err(|e| Error::InvalidModelsPathTemplate {
template: template.to_string(),
source: e,
})?;
Ok(Self::from_template(template))
}
pub(crate) fn from_template(template: &str) -> Self {
Self {
template: template.to_string(),
}
}
pub fn try_resolve(&self, service: &str, version: &str) -> Result<syn::Path> {
let path = self.substitute(service, version);
syn::parse_str(&path).map_err(|source| Error::InvalidModelsPathTemplate {
template: path,
source,
})
}
pub fn validate_for(&self, service: &str) -> Result<()> {
self.try_resolve(service, "v1").map(|_| ())
}
pub fn resolve(&self, service: &str, version: &str) -> syn::Path {
let path = self.substitute(service, version);
syn::parse_str(&path)
.unwrap_or_else(|e| panic!("Invalid models path `{path}` after substitution: {e}"))
}
fn substitute(&self, service: &str, version: &str) -> String {
self.template
.replace("{service}", service)
.replace("{version}", version)
}
}
#[derive(Debug, Clone)]
pub struct BindingsConfig {
pub aggregate_client_name: String,
pub client_crate_name: String,
pub py_error_type: String,
pub py_result_type: String,
pub napi_error_ext_trait: String,
pub typings_package_filter: Option<String>,
pub ts_error_base_class: String,
pub ts_error_code_prefix: String,
}
#[derive(Debug, Clone)]
pub struct CodeGenConfig {
pub context_type_path: String,
pub result_type_path: String,
pub models_path_template: String,
pub models_path_crate_template: String,
pub output: CodeGenOutput,
pub generate_resource_enum: bool,
pub generate_store_integration: bool,
pub error_type_path: Option<String>,
pub generate_object_conversions: bool,
pub bindings: Option<BindingsConfig>,
pub resource_store_crate_name: String,
pub runtime: Runtime,
pub transport_type_path: String,
pub dual_transport: bool,
pub client_protocols: ClientProtocols,
pub connect_client_path: Option<String>,
}
pub const DEFAULT_TRANSPORT_TYPE_PATH: &str = "olai_http::CloudClient";
impl CodeGenConfig {
pub fn uses_default_transport(&self) -> bool {
self.transport_type_path == DEFAULT_TRANSPORT_TYPE_PATH
}
pub fn dual_transport(&self) -> bool {
self.output.wasm.is_some() || self.dual_transport
}
pub fn validate(&self) -> Result<()> {
ModelsPath::new(&self.models_path_template)?;
ModelsPath::new(&self.models_path_crate_template)?;
let wants_bindings = self.output.python.is_some()
|| self.output.node.is_some()
|| self.output.node_ts.is_some()
|| self.output.wasm.is_some();
if wants_bindings && self.bindings.is_none() {
return Err(Error::MissingBindingsConfig);
}
if self.client_protocols.connect
&& self.output.client.is_some()
&& self.connect_client_path.is_none()
{
return Err(Error::MissingConnectClientPath);
}
if wants_bindings && !self.client_protocols.rest {
return Err(Error::BindingsRequireRest);
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct CodeGenOutput {
pub common: PathBuf,
pub models: Option<PathBuf>,
pub models_subdir: String,
pub server: Option<PathBuf>,
pub client: Option<PathBuf>,
pub generate_resource_clients: bool,
pub python: Option<PathBuf>,
pub node: Option<PathBuf>,
pub node_ts: Option<PathBuf>,
pub wasm: Option<PathBuf>,
pub python_typings_filename: String,
}
impl CodeGenOutput {
pub fn models_subdir_path(&self) -> Option<PathBuf> {
self.models.as_ref().map(|m| m.join(&self.models_subdir))
}
}