mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-23 17:08:10 -07:00
clients: snapshot
This commit is contained in:
@@ -97,33 +97,24 @@ pub fn generate_tree_node_field<S: LanguageSyntax>(
|
||||
let type_ann = metadata.field_type_annotation(field, false, None, syntax.generic_syntax());
|
||||
|
||||
let value = if metadata.is_pattern_type(&field.rust_type) {
|
||||
// Check if this pattern is parameterizable
|
||||
let pattern = metadata.find_pattern(&field.rust_type);
|
||||
let is_parameterizable = pattern.is_some_and(|p| p.is_parameterizable());
|
||||
// Use metric base only for parameterizable patterns
|
||||
let use_base = metadata
|
||||
.find_pattern(&field.rust_type)
|
||||
.is_some_and(|p| p.is_parameterizable())
|
||||
&& pattern_base.is_some();
|
||||
|
||||
if is_parameterizable {
|
||||
if let Some(base) = pattern_base {
|
||||
// Use the detected metric base
|
||||
let path = syntax.string_literal(base);
|
||||
syntax.constructor(&field.rust_type, &path)
|
||||
} else {
|
||||
// Fallback to tree path
|
||||
let path_expr = syntax.path_expr("base_path", &path_suffix(child_name));
|
||||
syntax.constructor(&field.rust_type, &path_expr)
|
||||
}
|
||||
let path_arg = if use_base {
|
||||
syntax.string_literal(pattern_base.unwrap())
|
||||
} else {
|
||||
let path_expr = syntax.path_expr("base_path", &path_suffix(child_name));
|
||||
syntax.constructor(&field.rust_type, &path_expr)
|
||||
}
|
||||
syntax.path_expr("base_path", &path_suffix(child_name))
|
||||
};
|
||||
syntax.constructor(&field.rust_type, &path_arg)
|
||||
} else if let Some(accessor) = metadata.find_index_set_pattern(&field.indexes) {
|
||||
// Leaf field - use actual metric name if provided
|
||||
if let Some(metric_name) = pattern_base {
|
||||
let path = syntax.string_literal(metric_name);
|
||||
syntax.constructor(&accessor.name, &path)
|
||||
} else {
|
||||
let path_expr = syntax.path_expr("base_path", &path_suffix(child_name));
|
||||
syntax.constructor(&accessor.name, &path_expr)
|
||||
}
|
||||
// Leaf field - use metric name if provided, else tree path
|
||||
let path_arg = pattern_base
|
||||
.map(|name| syntax.string_literal(name))
|
||||
.unwrap_or_else(|| syntax.path_expr("base_path", &path_suffix(child_name)));
|
||||
syntax.constructor(&accessor.name, &path_arg)
|
||||
} else if field.is_branch() {
|
||||
// Non-pattern branch - instantiate the nested struct
|
||||
let path_expr = syntax.path_expr("base_path", &path_suffix(child_name));
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::fmt::Write;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{TypeSchemas, generators::{MANUAL_GENERIC_TYPES, write_description}, ref_to_type_name, to_camel_case};
|
||||
use crate::{TypeSchemas, generators::{MANUAL_GENERIC_TYPES, write_description}, get_union_variants, ref_to_type_name, to_camel_case};
|
||||
|
||||
/// Generate JSDoc type definitions from OpenAPI schemas.
|
||||
pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
||||
@@ -165,11 +165,7 @@ pub fn schema_to_js_type(schema: &Value, current_type: Option<&str>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(variants) = schema
|
||||
.get("anyOf")
|
||||
.or_else(|| schema.get("oneOf"))
|
||||
.and_then(|v| v.as_array())
|
||||
{
|
||||
if let Some(variants) = get_union_variants(schema) {
|
||||
let types: Vec<String> = variants
|
||||
.iter()
|
||||
.map(|v| schema_to_js_type(v, current_type))
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::fmt::Write;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{TypeSchemas, escape_python_keyword, generators::MANUAL_GENERIC_TYPES, ref_to_type_name};
|
||||
use crate::{TypeSchemas, escape_python_keyword, generators::MANUAL_GENERIC_TYPES, get_union_variants, ref_to_type_name};
|
||||
|
||||
/// Generate type definitions from schemas.
|
||||
pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
||||
@@ -36,7 +36,7 @@ pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
||||
for name in type_aliases {
|
||||
let schema = &schemas[&name];
|
||||
let type_desc = schema.get("description").and_then(|d| d.as_str());
|
||||
let py_type = schema_to_python_type_quoting(schema, Some(&name), &typed_dict_set);
|
||||
let py_type = schema_to_python_type(schema, Some(&name), Some(&typed_dict_set));
|
||||
if let Some(desc) = type_desc {
|
||||
for line in desc.lines() {
|
||||
writeln!(output, "# {}", line).unwrap();
|
||||
@@ -87,7 +87,7 @@ pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
||||
}
|
||||
|
||||
for (prop_name, prop_schema) in props {
|
||||
let prop_type = schema_to_python_type_ctx(prop_schema, Some(&name));
|
||||
let prop_type = schema_to_python_type(prop_schema, Some(&name), None);
|
||||
let safe_name = escape_python_keyword(prop_name);
|
||||
writeln!(output, " {}: {}", safe_name, prop_type).unwrap();
|
||||
}
|
||||
@@ -193,13 +193,13 @@ fn json_type_to_python(ty: &str, schema: &Value, current_type: Option<&str>) ->
|
||||
"array" => {
|
||||
let item_type = schema
|
||||
.get("items")
|
||||
.map(|s| schema_to_python_type_ctx(s, current_type))
|
||||
.map(|s| schema_to_python_type(s, current_type, None))
|
||||
.unwrap_or_else(|| "Any".to_string());
|
||||
format!("List[{}]", item_type)
|
||||
}
|
||||
"object" => {
|
||||
if let Some(add_props) = schema.get("additionalProperties") {
|
||||
let value_type = schema_to_python_type_ctx(add_props, current_type);
|
||||
let value_type = schema_to_python_type(add_props, current_type, None);
|
||||
return format!("dict[str, {}]", value_type);
|
||||
}
|
||||
"dict".to_string()
|
||||
@@ -208,15 +208,18 @@ fn json_type_to_python(ty: &str, schema: &Value, current_type: Option<&str>) ->
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert JSON Schema to Python type, quoting types in the given set
|
||||
fn schema_to_python_type_quoting(
|
||||
/// Convert JSON Schema to Python type.
|
||||
///
|
||||
/// - `current_type`: Used to detect and quote self-references for recursive types
|
||||
/// - `quote_types`: Optional set of additional type names that should be quoted
|
||||
pub fn schema_to_python_type(
|
||||
schema: &Value,
|
||||
current_type: Option<&str>,
|
||||
quote_types: &HashSet<String>,
|
||||
quote_types: Option<&HashSet<String>>,
|
||||
) -> String {
|
||||
if let Some(all_of) = schema.get("allOf").and_then(|v| v.as_array()) {
|
||||
for item in all_of {
|
||||
let resolved = schema_to_python_type_quoting(item, current_type, quote_types);
|
||||
let resolved = schema_to_python_type(item, current_type, quote_types);
|
||||
if resolved != "Any" {
|
||||
return resolved;
|
||||
}
|
||||
@@ -227,67 +230,9 @@ fn schema_to_python_type_quoting(
|
||||
if let Some(ref_path) = schema.get("$ref").and_then(|r| r.as_str()) {
|
||||
let type_name = ref_to_type_name(ref_path).unwrap_or("Any");
|
||||
// Quote self-references or types in quote_types set
|
||||
if current_type == Some(type_name) || quote_types.contains(type_name) {
|
||||
return format!("\"{}\"", type_name);
|
||||
}
|
||||
return type_name.to_string();
|
||||
}
|
||||
|
||||
// Handle enum (array of string values)
|
||||
if let Some(enum_values) = schema.get("enum").and_then(|e| e.as_array()) {
|
||||
let literals: Vec<String> = enum_values
|
||||
.iter()
|
||||
.filter_map(|v| v.as_str())
|
||||
.map(|s| format!("\"{}\"", s))
|
||||
.collect();
|
||||
if !literals.is_empty() {
|
||||
return format!("Literal[{}]", literals.join(", "));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(variants) = schema
|
||||
.get("anyOf")
|
||||
.or_else(|| schema.get("oneOf"))
|
||||
.and_then(|v| v.as_array())
|
||||
{
|
||||
let types: Vec<String> = variants
|
||||
.iter()
|
||||
.map(|v| schema_to_python_type_quoting(v, current_type, quote_types))
|
||||
.collect();
|
||||
let filtered: Vec<_> = types.iter().filter(|t| *t != "Any").collect();
|
||||
if !filtered.is_empty() {
|
||||
return format!(
|
||||
"Union[{}]",
|
||||
filtered
|
||||
.iter()
|
||||
.map(|s| s.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
}
|
||||
return format!("Union[{}]", types.join(", "));
|
||||
}
|
||||
|
||||
// Fall back to regular conversion for other cases
|
||||
schema_to_python_type_ctx(schema, current_type)
|
||||
}
|
||||
|
||||
/// Convert JSON Schema to Python type with context for detecting self-references
|
||||
pub fn schema_to_python_type_ctx(schema: &Value, current_type: Option<&str>) -> String {
|
||||
if let Some(all_of) = schema.get("allOf").and_then(|v| v.as_array()) {
|
||||
for item in all_of {
|
||||
let resolved = schema_to_python_type_ctx(item, current_type);
|
||||
if resolved != "Any" {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle $ref
|
||||
if let Some(ref_path) = schema.get("$ref").and_then(|r| r.as_str()) {
|
||||
let type_name = ref_to_type_name(ref_path).unwrap_or("Any");
|
||||
// Quote self-references to handle recursive types
|
||||
if current_type == Some(type_name) {
|
||||
let should_quote = current_type == Some(type_name)
|
||||
|| quote_types.is_some_and(|qt| qt.contains(type_name));
|
||||
if should_quote {
|
||||
return format!("\"{}\"", type_name);
|
||||
}
|
||||
return type_name.to_string();
|
||||
@@ -310,7 +255,7 @@ pub fn schema_to_python_type_ctx(schema: &Value, current_type: Option<&str>) ->
|
||||
let types: Vec<String> = type_array
|
||||
.iter()
|
||||
.filter_map(|t| t.as_str())
|
||||
.filter(|t| *t != "null") // Filter out null for cleaner Optional handling
|
||||
.filter(|t| *t != "null")
|
||||
.map(|t| json_type_to_python(t, schema, current_type))
|
||||
.collect();
|
||||
let has_null = type_array.iter().any(|t| t.as_str() == Some("null"));
|
||||
@@ -337,14 +282,10 @@ pub fn schema_to_python_type_ctx(schema: &Value, current_type: Option<&str>) ->
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(variants) = schema
|
||||
.get("anyOf")
|
||||
.or_else(|| schema.get("oneOf"))
|
||||
.and_then(|v| v.as_array())
|
||||
{
|
||||
if let Some(variants) = get_union_variants(schema) {
|
||||
let types: Vec<String> = variants
|
||||
.iter()
|
||||
.map(|v| schema_to_python_type_ctx(v, current_type))
|
||||
.map(|v| schema_to_python_type(v, current_type, quote_types))
|
||||
.collect();
|
||||
let filtered: Vec<_> = types.iter().filter(|t| *t != "Any").collect();
|
||||
if !filtered.is_empty() {
|
||||
|
||||
@@ -165,24 +165,17 @@ pub fn extract_endpoints(spec: &Spec) -> Vec<Endpoint> {
|
||||
endpoints
|
||||
}
|
||||
|
||||
fn get_operations(path_item: &PathItem) -> Vec<(String, &Operation)> {
|
||||
let mut ops = Vec::new();
|
||||
if let Some(op) = &path_item.get {
|
||||
ops.push(("GET".to_string(), op));
|
||||
}
|
||||
if let Some(op) = &path_item.post {
|
||||
ops.push(("POST".to_string(), op));
|
||||
}
|
||||
if let Some(op) = &path_item.put {
|
||||
ops.push(("PUT".to_string(), op));
|
||||
}
|
||||
if let Some(op) = &path_item.delete {
|
||||
ops.push(("DELETE".to_string(), op));
|
||||
}
|
||||
if let Some(op) = &path_item.patch {
|
||||
ops.push(("PATCH".to_string(), op));
|
||||
}
|
||||
ops
|
||||
fn get_operations(path_item: &PathItem) -> Vec<(&'static str, &Operation)> {
|
||||
[
|
||||
("GET", &path_item.get),
|
||||
("POST", &path_item.post),
|
||||
("PUT", &path_item.put),
|
||||
("DELETE", &path_item.delete),
|
||||
("PATCH", &path_item.patch),
|
||||
]
|
||||
.into_iter()
|
||||
.filter_map(|(method, op)| op.as_ref().map(|o| (method, o)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn extract_endpoint(path: &str, method: &str, operation: &Operation) -> Option<Endpoint> {
|
||||
|
||||
@@ -33,3 +33,11 @@ pub fn extract_inner_type(type_str: &str) -> String {
|
||||
pub fn ref_to_type_name(ref_path: &str) -> Option<&str> {
|
||||
ref_path.rsplit('/').next()
|
||||
}
|
||||
|
||||
/// Get union variants from anyOf or oneOf schema.
|
||||
pub fn get_union_variants(schema: &Value) -> Option<&Vec<Value>> {
|
||||
schema
|
||||
.get("anyOf")
|
||||
.or_else(|| schema.get("oneOf"))
|
||||
.and_then(|v| v.as_array())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user