binder: snapshot

This commit is contained in:
nym21
2025-12-20 19:33:04 +01:00
parent 8f19bf7350
commit bcb8d5bed6
6 changed files with 841 additions and 162 deletions
+187 -64
View File
@@ -8,8 +8,8 @@ use brk_types::{Index, TreeNode};
use serde_json::Value;
use crate::{
ClientMetadata, Endpoint, IndexSetPattern, PatternField, StructuralPattern, TypeSchemas,
get_node_fields, to_pascal_case, to_snake_case,
ClientMetadata, Endpoint, FieldNamePosition, IndexSetPattern, PatternField, StructuralPattern,
TypeSchemas, get_node_fields, get_pattern_instance_base, to_pascal_case, to_snake_case,
};
/// Generate Python client from metadata and OpenAPI endpoints
@@ -253,6 +253,8 @@ fn generate_structural_patterns(
writeln!(output, "# Reusable structural pattern classes\n").unwrap();
for pattern in patterns {
let is_parameterizable = pattern.is_parameterizable();
writeln!(output, "class {}:", pattern.name).unwrap();
writeln!(
output,
@@ -260,44 +262,27 @@ fn generate_structural_patterns(
)
.unwrap();
writeln!(output, " ").unwrap();
writeln!(
output,
" def __init__(self, client: BrkClientBase, base_path: str):"
)
.unwrap();
if is_parameterizable {
writeln!(
output,
" def __init__(self, client: BrkClientBase, acc: str):"
)
.unwrap();
writeln!(output, " \"\"\"Create pattern node with accumulated metric name.\"\"\"").unwrap();
} else {
writeln!(
output,
" def __init__(self, client: BrkClientBase, base_path: str):"
)
.unwrap();
}
for field in &pattern.fields {
let py_type = field_to_python_type(field, metadata);
if metadata.is_pattern_type(&field.rust_type) {
writeln!(
output,
" self.{}: {} = {}(client, f'{{base_path}}/{}')",
to_snake_case(&field.name),
py_type,
field.rust_type,
field.name
)
.unwrap();
} else if field_uses_accessor(field, metadata) {
let accessor = metadata.find_index_set_pattern(&field.indexes).unwrap();
writeln!(
output,
" self.{}: {} = {}(client, f'{{base_path}}/{}')",
to_snake_case(&field.name),
py_type,
accessor.name,
field.name
)
.unwrap();
if is_parameterizable {
generate_parameterized_python_field(output, field, pattern, metadata);
} else {
writeln!(
output,
" self.{}: {} = MetricNode(client, f'{{base_path}}/{}')",
to_snake_case(&field.name),
py_type,
field.name
)
.unwrap();
generate_tree_path_python_field(output, field, metadata);
}
}
@@ -305,6 +290,102 @@ fn generate_structural_patterns(
}
}
/// Generate a field using parameterized (prepend/append) metric name construction
fn generate_parameterized_python_field(
output: &mut String,
field: &PatternField,
pattern: &StructuralPattern,
metadata: &ClientMetadata,
) {
let field_name = to_snake_case(&field.name);
let py_type = field_to_python_type(field, metadata);
// For branch fields, pass the accumulated name to nested pattern
if metadata.is_pattern_type(&field.rust_type) {
let child_acc = if let Some(pos) = pattern.get_field_position(&field.name) {
match pos {
FieldNamePosition::Append(suffix) => format!("f'{{acc}}{}'", suffix),
FieldNamePosition::Prepend(prefix) => format!("f'{}{{acc}}'", prefix),
FieldNamePosition::Identity => "acc".to_string(),
FieldNamePosition::SetBase(base) => format!("'{}'", base),
}
} else {
format!("f'{{acc}}_{}'", field.name)
};
writeln!(
output,
" self.{}: {} = {}(client, {})",
field_name, py_type, field.rust_type, child_acc
)
.unwrap();
return;
}
// For leaf fields, construct the metric path based on position
let metric_expr = if let Some(pos) = pattern.get_field_position(&field.name) {
match pos {
FieldNamePosition::Append(suffix) => format!("f'/{{acc}}{}'", suffix),
FieldNamePosition::Prepend(prefix) => format!("f'/{}{{acc}}'", prefix),
FieldNamePosition::Identity => "f'/{acc}'".to_string(),
FieldNamePosition::SetBase(base) => format!("'/{}'", base),
}
} else {
format!("f'/{{acc}}_{}'", field.name)
};
if field_uses_accessor(field, metadata) {
let accessor = metadata.find_index_set_pattern(&field.indexes).unwrap();
writeln!(
output,
" self.{}: {} = {}(client, {})",
field_name, py_type, accessor.name, metric_expr
)
.unwrap();
} else {
writeln!(
output,
" self.{}: {} = MetricNode(client, {})",
field_name, py_type, metric_expr
)
.unwrap();
}
}
/// Generate a field using tree path construction (fallback for non-parameterizable patterns)
fn generate_tree_path_python_field(
output: &mut String,
field: &PatternField,
metadata: &ClientMetadata,
) {
let field_name = to_snake_case(&field.name);
let py_type = field_to_python_type(field, metadata);
if metadata.is_pattern_type(&field.rust_type) {
writeln!(
output,
" self.{}: {} = {}(client, f'{{base_path}}/{}')",
field_name, py_type, field.rust_type, field.name
)
.unwrap();
} else if field_uses_accessor(field, metadata) {
let accessor = metadata.find_index_set_pattern(&field.indexes).unwrap();
writeln!(
output,
" self.{}: {} = {}(client, f'{{base_path}}/{}')",
field_name, py_type, accessor.name, field.name
)
.unwrap();
} else {
writeln!(
output,
" self.{}: {} = MetricNode(client, f'{{base_path}}/{}')",
field_name, py_type, field.name
)
.unwrap();
}
}
/// Convert pattern field to Python type annotation
fn field_to_python_type(field: &PatternField, metadata: &ClientMetadata) -> String {
if metadata.is_pattern_type(&field.rust_type) {
@@ -374,38 +455,80 @@ fn generate_tree_class(
)
.unwrap();
for field in &fields {
for (field, (child_name, child_node)) in fields.iter().zip(children.iter()) {
let py_type = field_to_python_type(field, metadata);
let field_name_py = to_snake_case(&field.name);
if metadata.is_pattern_type(&field.rust_type) {
writeln!(
output,
" self.{}: {} = {}(client, f'{{base_path}}/{}')",
to_snake_case(&field.name),
py_type,
field.rust_type,
field.name
)
.unwrap();
// Check if the pattern is parameterizable
let pattern = metadata
.structural_patterns
.iter()
.find(|p| p.name == field.rust_type);
let is_parameterizable = pattern.map(|p| p.is_parameterizable()).unwrap_or(false);
if is_parameterizable {
// Get the metric base from the first leaf descendant
let metric_base = get_pattern_instance_base(child_node, child_name);
writeln!(
output,
" self.{}: {} = {}(client, '{}')",
field_name_py, py_type, field.rust_type, metric_base
)
.unwrap();
} else {
writeln!(
output,
" self.{}: {} = {}(client, f'{{base_path}}/{}')",
field_name_py, py_type, field.rust_type, field.name
)
.unwrap();
}
} else if field_uses_accessor(field, metadata) {
// Leaf with accessor - get actual metric path from leaf
let metric_path = if let TreeNode::Leaf(leaf) = child_node {
format!("/{}", leaf.name())
} else {
format!("{{base_path}}/{}", field.name)
};
let accessor = metadata.find_index_set_pattern(&field.indexes).unwrap();
writeln!(
output,
" self.{}: {} = {}(client, f'{{base_path}}/{}')",
to_snake_case(&field.name),
py_type,
accessor.name,
field.name
)
.unwrap();
if metric_path.contains("{base_path}") {
writeln!(
output,
" self.{}: {} = {}(client, f'{}')",
field_name_py, py_type, accessor.name, metric_path
)
.unwrap();
} else {
writeln!(
output,
" self.{}: {} = {}(client, '{}')",
field_name_py, py_type, accessor.name, metric_path
)
.unwrap();
}
} else {
writeln!(
output,
" self.{}: {} = MetricNode(client, f'{{base_path}}/{}')",
to_snake_case(&field.name),
py_type,
field.name
)
.unwrap();
// Leaf without accessor - get actual metric path from leaf
let metric_path = if let TreeNode::Leaf(leaf) = child_node {
format!("/{}", leaf.name())
} else {
format!("{{base_path}}/{}", field.name)
};
if metric_path.contains("{base_path}") {
writeln!(
output,
" self.{}: {} = MetricNode(client, f'{}')",
field_name_py, py_type, metric_path
)
.unwrap();
} else {
writeln!(
output,
" self.{}: {} = MetricNode(client, '{}')",
field_name_py, py_type, metric_path
)
.unwrap();
}
}
}