#![allow(clippy::type_complexity)]
use std::{collections::btree_map::Entry, fs::create_dir_all, io, path::PathBuf};
use brk_query::Vecs;
#[derive(Debug, Clone, Default)]
pub struct ClientOutputPaths {
pub rust: Option<PathBuf>,
pub javascript: Option<PathBuf>,
pub python: Option<PathBuf>,
}
impl ClientOutputPaths {
pub fn new() -> Self {
Self::default()
}
pub fn rust(mut self, path: impl Into<PathBuf>) -> Self {
self.rust = Some(path.into());
self
}
pub fn javascript(mut self, path: impl Into<PathBuf>) -> Self {
self.javascript = Some(path.into());
self
}
pub fn python(mut self, path: impl Into<PathBuf>) -> Self {
self.python = Some(path.into());
self
}
}
mod analysis;
mod backends;
mod generate;
mod generators;
mod openapi;
mod syntax;
mod types;
pub use analysis::*;
pub use backends::*;
pub use generate::*;
pub use generators::*;
pub use openapi::*;
pub use syntax::*;
pub use types::*;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn generate_clients(
vecs: &Vecs,
openapi_json: &str,
output_paths: &ClientOutputPaths,
) -> io::Result<()> {
let metadata = ClientMetadata::from_vecs(vecs);
let spec = parse_openapi_json(openapi_json)?;
let endpoints = extract_endpoints(&spec);
let mut schemas = extract_schemas(openapi_json);
collect_leaf_type_schemas(&metadata.catalog, &mut schemas);
let schema_values: Vec<_> = schemas.values().cloned().collect();
for schema in &schema_values {
collect_schema_definitions(schema, &mut schemas);
}
if let Some(rust_path) = &output_paths.rust {
if let Some(parent) = rust_path.parent() {
create_dir_all(parent)?;
}
generate_rust_client(&metadata, &endpoints, rust_path)?;
}
if let Some(js_path) = &output_paths.javascript {
if let Some(parent) = js_path.parent() {
create_dir_all(parent)?;
}
generate_javascript_client(&metadata, &endpoints, &schemas, js_path)?;
}
if let Some(python_path) = &output_paths.python {
if let Some(parent) = python_path.parent() {
create_dir_all(parent)?;
}
generate_python_client(&metadata, &endpoints, &schemas, python_path)?;
}
Ok(())
}
use brk_types::TreeNode;
use serde_json::Value;
fn collect_leaf_type_schemas(node: &TreeNode, schemas: &mut TypeSchemas) {
match node {
TreeNode::Leaf(leaf) => {
collect_schema_definitions(&leaf.schema, schemas);
let type_name = extract_inner_type(leaf.kind());
if let Entry::Vacant(e) = schemas.entry(type_name) {
let schema = unwrap_allof(&leaf.schema);
let has_type = schema.get("type").is_some();
let has_properties = schema.get("properties").is_some();
let has_enum = schema.get("enum").is_some() || schema.get("oneOf").is_some();
let is_ref = schema.get("$ref").is_some();
if has_type || has_properties || has_enum || is_ref {
e.insert(schema.clone());
}
}
}
TreeNode::Branch(children) => {
for child in children.values() {
collect_leaf_type_schemas(child, schemas);
}
}
}
}
fn collect_schema_definitions(schema: &Value, schemas: &mut TypeSchemas) {
for key in ["definitions", "$defs"] {
if let Some(defs) = schema.get(key).and_then(|d| d.as_object()) {
for (name, def_schema) in defs {
if !schemas.contains_key(name) {
schemas.insert(name.clone(), def_schema.clone());
}
}
}
}
}