mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-16 05:28:12 -07:00
bindgen: determinism
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
//! This module detects repeating tree structures and analyzes them
|
||||
//! using the bottom-up name deconstruction algorithm.
|
||||
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use brk_types::{TreeNode, extract_json_type};
|
||||
|
||||
@@ -13,25 +13,25 @@ use crate::{PatternBaseResult, PatternField, StructuralPattern, to_pascal_case};
|
||||
/// Context for pattern detection, holding all intermediate state.
|
||||
struct PatternContext {
|
||||
/// Maps field signatures to pattern names
|
||||
signature_to_pattern: HashMap<Vec<PatternField>, String>,
|
||||
signature_to_pattern: BTreeMap<Vec<PatternField>, String>,
|
||||
/// Counts how many times each signature appears
|
||||
signature_counts: HashMap<Vec<PatternField>, usize>,
|
||||
signature_counts: BTreeMap<Vec<PatternField>, usize>,
|
||||
/// Maps normalized signatures to pattern names (for naming consistency)
|
||||
normalized_to_name: HashMap<Vec<PatternField>, String>,
|
||||
normalized_to_name: BTreeMap<Vec<PatternField>, String>,
|
||||
/// Counts pattern name usage (for unique naming)
|
||||
name_counts: HashMap<String, usize>,
|
||||
name_counts: BTreeMap<String, usize>,
|
||||
/// Maps signatures to their child field lists
|
||||
signature_to_child_fields: HashMap<Vec<PatternField>, Vec<Vec<PatternField>>>,
|
||||
signature_to_child_fields: BTreeMap<Vec<PatternField>, Vec<Vec<PatternField>>>,
|
||||
}
|
||||
|
||||
impl PatternContext {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
signature_to_pattern: HashMap::new(),
|
||||
signature_counts: HashMap::new(),
|
||||
normalized_to_name: HashMap::new(),
|
||||
name_counts: HashMap::new(),
|
||||
signature_to_child_fields: HashMap::new(),
|
||||
signature_to_pattern: BTreeMap::new(),
|
||||
signature_counts: BTreeMap::new(),
|
||||
normalized_to_name: BTreeMap::new(),
|
||||
name_counts: BTreeMap::new(),
|
||||
signature_to_child_fields: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,9 +45,9 @@ pub fn detect_structural_patterns(
|
||||
tree: &TreeNode,
|
||||
) -> (
|
||||
Vec<StructuralPattern>,
|
||||
HashMap<Vec<PatternField>, String>,
|
||||
HashMap<Vec<PatternField>, String>,
|
||||
HashMap<String, PatternBaseResult>,
|
||||
BTreeMap<Vec<PatternField>, String>,
|
||||
BTreeMap<Vec<PatternField>, String>,
|
||||
BTreeMap<String, PatternBaseResult>,
|
||||
) {
|
||||
let mut ctx = PatternContext::new();
|
||||
resolve_branch_patterns(tree, "root", &mut ctx);
|
||||
@@ -90,7 +90,7 @@ pub fn detect_structural_patterns(
|
||||
patterns.extend(generic_patterns);
|
||||
|
||||
// Build pattern lookup for mode analysis (patterns appearing 2+ times)
|
||||
let mut pattern_lookup: HashMap<Vec<PatternField>, String> = HashMap::new();
|
||||
let mut pattern_lookup: BTreeMap<Vec<PatternField>, String> = BTreeMap::new();
|
||||
for (sig, name) in &ctx.signature_to_pattern {
|
||||
if ctx.signature_counts.get(sig).copied().unwrap_or(0) >= 2 {
|
||||
pattern_lookup.insert(sig.clone(), name.clone());
|
||||
@@ -110,29 +110,30 @@ pub fn detect_structural_patterns(
|
||||
|
||||
/// Detect generic patterns by grouping signatures by their normalized form.
|
||||
fn detect_generic_patterns(
|
||||
signature_to_pattern: &HashMap<Vec<PatternField>, String>,
|
||||
signature_to_pattern: &BTreeMap<Vec<PatternField>, String>,
|
||||
) -> (
|
||||
Vec<StructuralPattern>,
|
||||
HashMap<Vec<PatternField>, String>,
|
||||
HashMap<Vec<PatternField>, String>,
|
||||
BTreeMap<Vec<PatternField>, String>,
|
||||
BTreeMap<Vec<PatternField>, String>,
|
||||
) {
|
||||
let mut normalized_groups: HashMap<
|
||||
let mut normalized_groups: BTreeMap<
|
||||
Vec<PatternField>,
|
||||
Vec<(Vec<PatternField>, String, String)>,
|
||||
> = HashMap::new();
|
||||
> = BTreeMap::new();
|
||||
|
||||
for (fields, name) in signature_to_pattern {
|
||||
if let Some((normalized, extracted_type)) = normalize_fields_for_generic(fields) {
|
||||
normalized_groups
|
||||
.entry(normalized)
|
||||
.or_default()
|
||||
.push((fields.clone(), name.clone(), extracted_type));
|
||||
normalized_groups.entry(normalized).or_default().push((
|
||||
fields.clone(),
|
||||
name.clone(),
|
||||
extracted_type,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let mut patterns = Vec::new();
|
||||
let mut pattern_mappings: HashMap<Vec<PatternField>, String> = HashMap::new();
|
||||
let mut type_mappings: HashMap<Vec<PatternField>, String> = HashMap::new();
|
||||
let mut pattern_mappings: BTreeMap<Vec<PatternField>, String> = BTreeMap::new();
|
||||
let mut type_mappings: BTreeMap<Vec<PatternField>, String> = BTreeMap::new();
|
||||
|
||||
for (normalized_fields, group) in normalized_groups {
|
||||
if group.len() >= 2 {
|
||||
@@ -205,7 +206,10 @@ fn normalize_fields_for_generic(fields: &[PatternField]) -> Option<(Vec<PatternF
|
||||
// Only proceed if inner types differ from originals (meaning they had wrappers)
|
||||
// and all inner types are the same
|
||||
if inner_types.iter().all(|t| t == first_inner)
|
||||
&& inner_types.iter().zip(leaf_types.iter()).any(|(inner, orig)| inner != *orig)
|
||||
&& inner_types
|
||||
.iter()
|
||||
.zip(leaf_types.iter())
|
||||
.any(|(inner, orig)| inner != *orig)
|
||||
{
|
||||
let normalized = fields
|
||||
.iter()
|
||||
@@ -301,7 +305,8 @@ fn resolve_branch_patterns(
|
||||
.entry(normalized)
|
||||
.or_insert_with(|| generate_pattern_name(field_name, &mut ctx.name_counts))
|
||||
.clone();
|
||||
ctx.signature_to_pattern.insert(fields.clone(), name.clone());
|
||||
ctx.signature_to_pattern
|
||||
.insert(fields.clone(), name.clone());
|
||||
name
|
||||
};
|
||||
|
||||
@@ -329,7 +334,7 @@ fn normalize_fields_for_naming(fields: &[PatternField]) -> Vec<PatternField> {
|
||||
}
|
||||
|
||||
/// Generate a unique pattern name.
|
||||
fn generate_pattern_name(field_name: &str, name_counts: &mut HashMap<String, usize>) -> String {
|
||||
fn generate_pattern_name(field_name: &str, name_counts: &mut BTreeMap<String, usize>) -> String {
|
||||
let pascal = to_pascal_case(field_name);
|
||||
let sanitized = if pascal.chars().next().is_some_and(|c| c.is_ascii_digit()) {
|
||||
format!("_{}", pascal)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! suffix mode (fields append to acc) or prefix mode (fields prepend to acc),
|
||||
//! and extracts the field parts (relatives or prefixes) for code generation.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use brk_types::TreeNode;
|
||||
|
||||
@@ -18,7 +18,7 @@ struct InstanceAnalysis {
|
||||
base: String,
|
||||
/// For suffix mode: field -> relative name
|
||||
/// For prefix mode: field -> prefix
|
||||
field_parts: HashMap<String, String>,
|
||||
field_parts: BTreeMap<String, String>,
|
||||
/// Whether this instance appears to be suffix mode
|
||||
is_suffix_mode: bool,
|
||||
}
|
||||
@@ -34,12 +34,12 @@ struct InstanceAnalysis {
|
||||
pub fn analyze_pattern_modes(
|
||||
tree: &TreeNode,
|
||||
patterns: &mut [StructuralPattern],
|
||||
pattern_lookup: &HashMap<Vec<PatternField>, String>,
|
||||
) -> HashMap<String, PatternBaseResult> {
|
||||
pattern_lookup: &BTreeMap<Vec<PatternField>, String>,
|
||||
) -> BTreeMap<String, PatternBaseResult> {
|
||||
// Collect analyses from all instances, keyed by pattern name
|
||||
let mut all_analyses: HashMap<String, Vec<InstanceAnalysis>> = HashMap::new();
|
||||
let mut all_analyses: BTreeMap<String, Vec<InstanceAnalysis>> = BTreeMap::new();
|
||||
// Also collect base results for each node, keyed by tree path
|
||||
let mut node_bases: HashMap<String, PatternBaseResult> = HashMap::new();
|
||||
let mut node_bases: BTreeMap<String, PatternBaseResult> = BTreeMap::new();
|
||||
|
||||
// Bottom-up traversal
|
||||
collect_instance_analyses(tree, "", pattern_lookup, &mut all_analyses, &mut node_bases);
|
||||
@@ -61,9 +61,9 @@ pub fn analyze_pattern_modes(
|
||||
fn collect_instance_analyses(
|
||||
node: &TreeNode,
|
||||
path: &str,
|
||||
pattern_lookup: &HashMap<Vec<PatternField>, String>,
|
||||
all_analyses: &mut HashMap<String, Vec<InstanceAnalysis>>,
|
||||
node_bases: &mut HashMap<String, PatternBaseResult>,
|
||||
pattern_lookup: &BTreeMap<Vec<PatternField>, String>,
|
||||
all_analyses: &mut BTreeMap<String, Vec<InstanceAnalysis>>,
|
||||
node_bases: &mut BTreeMap<String, PatternBaseResult>,
|
||||
) -> Option<String> {
|
||||
match node {
|
||||
TreeNode::Leaf(leaf) => {
|
||||
@@ -72,7 +72,7 @@ fn collect_instance_analyses(
|
||||
}
|
||||
TreeNode::Branch(children) => {
|
||||
// First, process all children recursively (bottom-up)
|
||||
let mut child_bases: HashMap<String, String> = HashMap::new();
|
||||
let mut child_bases: BTreeMap<String, String> = BTreeMap::new();
|
||||
for (field_name, child_node) in children {
|
||||
let child_path = build_child_path(path, field_name);
|
||||
if let Some(base) = collect_instance_analyses(
|
||||
@@ -122,13 +122,13 @@ fn collect_instance_analyses(
|
||||
}
|
||||
|
||||
/// Analyze a single pattern instance from its child bases.
|
||||
fn analyze_instance(child_bases: &HashMap<String, String>) -> InstanceAnalysis {
|
||||
fn analyze_instance(child_bases: &BTreeMap<String, String>) -> InstanceAnalysis {
|
||||
let bases: Vec<&str> = child_bases.values().map(|s| s.as_str()).collect();
|
||||
|
||||
// Try suffix mode first: look for common prefix among children
|
||||
if let Some(common_prefix) = find_common_prefix(&bases) {
|
||||
let base = common_prefix.trim_end_matches('_').to_string();
|
||||
let mut field_parts = HashMap::new();
|
||||
let mut field_parts = BTreeMap::new();
|
||||
|
||||
for (field_name, child_base) in child_bases {
|
||||
// Relative = child_base with common prefix stripped
|
||||
@@ -154,7 +154,7 @@ fn analyze_instance(child_bases: &HashMap<String, String>) -> InstanceAnalysis {
|
||||
// Try prefix mode: look for common suffix among children
|
||||
if let Some(common_suffix) = find_common_suffix(&bases) {
|
||||
let base = common_suffix.trim_start_matches('_').to_string();
|
||||
let mut field_parts = HashMap::new();
|
||||
let mut field_parts = BTreeMap::new();
|
||||
|
||||
for (field_name, child_base) in child_bases {
|
||||
// Prefix = child_base with common suffix stripped, normalized to end with _
|
||||
@@ -214,8 +214,8 @@ fn determine_pattern_mode(
|
||||
}
|
||||
|
||||
// Find the most common field_parts within the majority group
|
||||
// Convert to sorted Vec for comparison since HashMap isn't hashable
|
||||
let mut parts_counts: HashMap<Vec<(String, String)>, usize> = HashMap::new();
|
||||
// Convert to sorted Vec for comparison since BTreeMap isn't hashable
|
||||
let mut parts_counts: BTreeMap<Vec<(String, String)>, usize> = BTreeMap::new();
|
||||
for analysis in &majority_instances {
|
||||
let mut sorted: Vec<_> = analysis
|
||||
.field_parts
|
||||
@@ -227,7 +227,7 @@ fn determine_pattern_mode(
|
||||
}
|
||||
|
||||
let (best_parts_vec, _count) = parts_counts.into_iter().max_by_key(|(_, count)| *count)?;
|
||||
let best_parts: HashMap<String, String> = best_parts_vec.into_iter().collect();
|
||||
let best_parts: BTreeMap<String, String> = best_parts_vec.into_iter().collect();
|
||||
|
||||
// Verify all required fields have parts
|
||||
for field in fields {
|
||||
@@ -255,7 +255,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_analyze_instance_suffix_mode() {
|
||||
let mut child_bases = HashMap::new();
|
||||
let mut child_bases = BTreeMap::new();
|
||||
child_bases.insert("max".to_string(), "lth_cost_basis_max".to_string());
|
||||
child_bases.insert("min".to_string(), "lth_cost_basis_min".to_string());
|
||||
child_bases.insert("percentiles".to_string(), "lth_cost_basis".to_string());
|
||||
@@ -276,7 +276,7 @@ mod tests {
|
||||
fn test_analyze_instance_prefix_mode() {
|
||||
// Period-prefixed metrics like "1y_lump_sum_stack", "1m_lump_sum_stack"
|
||||
// share a common suffix "_lump_sum_stack" with different period prefixes
|
||||
let mut child_bases = HashMap::new();
|
||||
let mut child_bases = BTreeMap::new();
|
||||
child_bases.insert("_1y".to_string(), "1y_lump_sum_stack".to_string());
|
||||
child_bases.insert("_1m".to_string(), "1m_lump_sum_stack".to_string());
|
||||
child_bases.insert("_1w".to_string(), "1w_lump_sum_stack".to_string());
|
||||
@@ -293,7 +293,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_analyze_instance_root_suffix() {
|
||||
// At root level with suffix naming convention
|
||||
let mut child_bases = HashMap::new();
|
||||
let mut child_bases = BTreeMap::new();
|
||||
child_bases.insert("max".to_string(), "cost_basis_max".to_string());
|
||||
child_bases.insert("min".to_string(), "cost_basis_min".to_string());
|
||||
child_bases.insert("percentiles".to_string(), "cost_basis".to_string());
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! This module provides utilities for working with the TreeNode structure,
|
||||
//! including leaf name extraction and index pattern detection.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use brk_types::{Index, TreeNode, extract_json_type};
|
||||
|
||||
@@ -28,7 +28,7 @@ fn get_shortest_leaf_name(node: &TreeNode) -> Option<String> {
|
||||
/// Get the field signature for a branch node's children.
|
||||
pub fn get_node_fields(
|
||||
children: &BTreeMap<String, TreeNode>,
|
||||
pattern_lookup: &HashMap<Vec<PatternField>, String>,
|
||||
pattern_lookup: &BTreeMap<Vec<PatternField>, String>,
|
||||
) -> Vec<PatternField> {
|
||||
let mut fields: Vec<PatternField> = children
|
||||
.iter()
|
||||
@@ -117,7 +117,7 @@ pub struct PatternBaseResult {
|
||||
pub is_suffix_mode: bool,
|
||||
/// The field parts (suffix in suffix mode, prefix in prefix mode) for each field.
|
||||
/// Used to check if instance field parts match the pattern's field parts.
|
||||
pub field_parts: HashMap<String, String>,
|
||||
pub field_parts: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
impl PatternBaseResult {
|
||||
@@ -128,7 +128,7 @@ impl PatternBaseResult {
|
||||
base: String::new(),
|
||||
has_outlier: true,
|
||||
is_suffix_mode: true,
|
||||
field_parts: HashMap::new(),
|
||||
field_parts: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ impl PatternBaseResult {
|
||||
base: String::new(),
|
||||
has_outlier: false,
|
||||
is_suffix_mode: true,
|
||||
field_parts: HashMap::new(),
|
||||
field_parts: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,7 +200,7 @@ struct FindBaseResult {
|
||||
base: String,
|
||||
has_outlier: bool,
|
||||
is_suffix_mode: bool,
|
||||
field_parts: HashMap<String, String>,
|
||||
field_parts: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
/// Try to find a common base from child names using prefix/suffix detection.
|
||||
@@ -214,7 +214,7 @@ fn try_find_base(
|
||||
// Try common prefix first (suffix mode)
|
||||
if let Some(prefix) = find_common_prefix(&leaf_names) {
|
||||
let base = prefix.trim_end_matches('_').to_string();
|
||||
let mut field_parts = HashMap::new();
|
||||
let mut field_parts = BTreeMap::new();
|
||||
for (field_name, leaf_name) in child_names {
|
||||
// Compute the suffix part for this field
|
||||
let suffix = if leaf_name == &base {
|
||||
@@ -238,7 +238,7 @@ fn try_find_base(
|
||||
// Try common suffix (prefix mode)
|
||||
if let Some(suffix) = find_common_suffix(&leaf_names) {
|
||||
let base = suffix.trim_start_matches('_').to_string();
|
||||
let mut field_parts = HashMap::new();
|
||||
let mut field_parts = BTreeMap::new();
|
||||
for (field_name, leaf_name) in child_names {
|
||||
// Compute the prefix part for this field, normalized to end with _
|
||||
let prefix_part = leaf_name
|
||||
@@ -300,7 +300,7 @@ pub fn infer_accumulated_name(parent_acc: &str, field_name: &str, descendant_lea
|
||||
pub fn get_fields_with_child_info(
|
||||
children: &BTreeMap<String, TreeNode>,
|
||||
parent_name: &str,
|
||||
pattern_lookup: &HashMap<Vec<PatternField>, String>,
|
||||
pattern_lookup: &BTreeMap<Vec<PatternField>, String>,
|
||||
) -> Vec<(PatternField, Option<Vec<PatternField>>)> {
|
||||
children
|
||||
.iter()
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
//! Shared tree generation helpers.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use brk_types::TreeNode;
|
||||
|
||||
use crate::{ClientMetadata, PatternBaseResult, PatternField, child_type_name, get_fields_with_child_info};
|
||||
use crate::{
|
||||
ClientMetadata, PatternBaseResult, PatternField, child_type_name, get_fields_with_child_info,
|
||||
};
|
||||
|
||||
/// Build a child path by appending a child name to a parent path.
|
||||
/// Uses "/" as separator. If parent is empty, returns just the child name.
|
||||
@@ -53,9 +55,9 @@ pub fn prepare_tree_node<'a>(
|
||||
node: &'a TreeNode,
|
||||
name: &str,
|
||||
path: &str,
|
||||
pattern_lookup: &HashMap<Vec<PatternField>, String>,
|
||||
pattern_lookup: &BTreeMap<Vec<PatternField>, String>,
|
||||
metadata: &ClientMetadata,
|
||||
generated: &mut HashSet<String>,
|
||||
generated: &mut BTreeSet<String>,
|
||||
) -> Option<TreeNodeContext<'a>> {
|
||||
let TreeNode::Branch(branch_children) = node else {
|
||||
return None;
|
||||
@@ -127,8 +129,8 @@ pub fn prepare_tree_node<'a>(
|
||||
|
||||
// should_inline determines if we generate an inline struct type
|
||||
// We inline if: it's a branch AND (doesn't match any pattern OR pattern incompatible OR has outlier)
|
||||
let should_inline =
|
||||
!is_leaf && (!matches_any_pattern || !pattern_compatible || base_result.has_outlier);
|
||||
let should_inline = !is_leaf
|
||||
&& (!matches_any_pattern || !pattern_compatible || base_result.has_outlier);
|
||||
|
||||
// Inline type name (only used when should_inline is true)
|
||||
let inline_type_name = if should_inline {
|
||||
|
||||
@@ -11,6 +11,7 @@ use std::{fmt::Write, fs, io, path::Path};
|
||||
|
||||
use serde_json::json;
|
||||
|
||||
use super::write_if_changed;
|
||||
use crate::{ClientMetadata, Endpoint, TypeSchemas, VERSION};
|
||||
|
||||
/// Generate JavaScript + JSDoc client from metadata and OpenAPI endpoints.
|
||||
@@ -34,7 +35,7 @@ pub fn generate_javascript_client(
|
||||
tree::generate_tree_typedefs(&mut output, &metadata.catalog, metadata);
|
||||
tree::generate_main_client(&mut output, &metadata.catalog, metadata, endpoints);
|
||||
|
||||
fs::write(output_path, output)?;
|
||||
write_if_changed(output_path, &output)?;
|
||||
|
||||
// Update package.json version if it exists in the same directory
|
||||
if let Some(parent) = output_path.parent() {
|
||||
@@ -59,7 +60,7 @@ fn update_package_json_version(package_json_path: &Path) -> io::Result<()> {
|
||||
let updated = serde_json::to_string_pretty(&package)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||
|
||||
fs::write(package_json_path, updated + "\n")?;
|
||||
write_if_changed(package_json_path, &(updated + "\n"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! JavaScript tree structure generation.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt::Write;
|
||||
|
||||
use brk_types::TreeNode;
|
||||
@@ -18,7 +18,7 @@ pub fn generate_tree_typedefs(output: &mut String, catalog: &TreeNode, metadata:
|
||||
writeln!(output, "// Catalog tree typedefs\n").unwrap();
|
||||
|
||||
let pattern_lookup = metadata.pattern_lookup();
|
||||
let mut generated = HashSet::new();
|
||||
let mut generated = BTreeSet::new();
|
||||
generate_tree_typedef(
|
||||
output,
|
||||
"MetricsTree",
|
||||
@@ -35,9 +35,9 @@ fn generate_tree_typedef(
|
||||
name: &str,
|
||||
path: &str,
|
||||
node: &TreeNode,
|
||||
pattern_lookup: &std::collections::HashMap<Vec<PatternField>, String>,
|
||||
pattern_lookup: &std::collections::BTreeMap<Vec<PatternField>, String>,
|
||||
metadata: &ClientMetadata,
|
||||
generated: &mut HashSet<String>,
|
||||
generated: &mut BTreeSet<String>,
|
||||
) {
|
||||
let Some(ctx) = prepare_tree_node(node, name, path, pattern_lookup, metadata, generated) else {
|
||||
return;
|
||||
@@ -124,7 +124,7 @@ pub fn generate_main_client(
|
||||
writeln!(output, " */").unwrap();
|
||||
writeln!(output, " _buildTree(basePath) {{").unwrap();
|
||||
writeln!(output, " return {{").unwrap();
|
||||
let mut generated = HashSet::new();
|
||||
let mut generated = BTreeSet::new();
|
||||
generate_tree_initializer(
|
||||
output,
|
||||
catalog,
|
||||
@@ -178,9 +178,9 @@ fn generate_tree_initializer(
|
||||
name: &str,
|
||||
path: &str,
|
||||
indent: usize,
|
||||
pattern_lookup: &std::collections::HashMap<Vec<PatternField>, String>,
|
||||
pattern_lookup: &std::collections::BTreeMap<Vec<PatternField>, String>,
|
||||
metadata: &ClientMetadata,
|
||||
generated: &mut HashSet<String>,
|
||||
generated: &mut BTreeSet<String>,
|
||||
) {
|
||||
let indent_str = " ".repeat(indent);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//! - `api.rs` - API method generation
|
||||
//! - `mod.rs` - Entry point
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::{fmt::Write, fs, io, path::Path};
|
||||
|
||||
pub mod javascript;
|
||||
pub mod python;
|
||||
@@ -41,3 +41,14 @@ pub fn normalize_return_type(return_type: &str) -> String {
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Write content to a file only if it differs from existing content.
|
||||
/// Preserves mtime when unchanged, avoiding unnecessary cargo rebuilds.
|
||||
pub fn write_if_changed(path: &Path, content: &str) -> io::Result<()> {
|
||||
if let Ok(existing) = fs::read_to_string(path)
|
||||
&& existing == content
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
fs::write(path, content)
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ pub mod client;
|
||||
pub mod tree;
|
||||
pub mod types;
|
||||
|
||||
use std::{fmt::Write, fs, io, path::Path};
|
||||
use std::{fmt::Write, io, path::Path};
|
||||
|
||||
use super::write_if_changed;
|
||||
use crate::{ClientMetadata, Endpoint, TypeSchemas};
|
||||
|
||||
/// Generate Python client from metadata and OpenAPI endpoints.
|
||||
@@ -48,7 +49,7 @@ pub fn generate_python_client(
|
||||
tree::generate_tree_classes(&mut output, &metadata.catalog, metadata);
|
||||
api::generate_main_client(&mut output, endpoints);
|
||||
|
||||
fs::write(output_path, output)?;
|
||||
write_if_changed(output_path, &output)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Python tree structure generation.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt::Write;
|
||||
|
||||
use brk_types::TreeNode;
|
||||
@@ -15,7 +15,7 @@ pub fn generate_tree_classes(output: &mut String, catalog: &TreeNode, metadata:
|
||||
writeln!(output, "# Metrics tree classes\n").unwrap();
|
||||
|
||||
let pattern_lookup = metadata.pattern_lookup();
|
||||
let mut generated = HashSet::new();
|
||||
let mut generated = BTreeSet::new();
|
||||
generate_tree_class(
|
||||
output,
|
||||
"MetricsTree",
|
||||
@@ -33,9 +33,9 @@ fn generate_tree_class(
|
||||
name: &str,
|
||||
path: &str,
|
||||
node: &TreeNode,
|
||||
pattern_lookup: &std::collections::HashMap<Vec<PatternField>, String>,
|
||||
pattern_lookup: &std::collections::BTreeMap<Vec<PatternField>, String>,
|
||||
metadata: &ClientMetadata,
|
||||
generated: &mut HashSet<String>,
|
||||
generated: &mut BTreeSet<String>,
|
||||
) {
|
||||
let Some(ctx) = prepare_tree_node(node, name, path, pattern_lookup, metadata, generated) else {
|
||||
return;
|
||||
@@ -74,7 +74,9 @@ fn generate_tree_class(
|
||||
|
||||
if child.is_leaf {
|
||||
if let TreeNode::Leaf(leaf) = child.node {
|
||||
generate_leaf_field(output, &syntax, "client", child.name, leaf, metadata, " ");
|
||||
generate_leaf_field(
|
||||
output, &syntax, "client", child.name, leaf, metadata, " ",
|
||||
);
|
||||
}
|
||||
} else if child.should_inline {
|
||||
// Inline class
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
//! Python type definitions generation.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt::Write;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{TypeSchemas, escape_python_keyword, generators::MANUAL_GENERIC_TYPES, get_union_variants, 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) {
|
||||
@@ -32,7 +35,7 @@ pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
||||
|
||||
// Generate simple type aliases first
|
||||
// Quote references to TypedDicts since they're defined after
|
||||
let typed_dict_set: HashSet<_> = typed_dicts.iter().cloned().collect();
|
||||
let typed_dict_set: BTreeSet<_> = typed_dicts.iter().cloned().collect();
|
||||
for name in type_aliases {
|
||||
let schema = &schemas[&name];
|
||||
let type_desc = schema.get("description").and_then(|d| d.as_str());
|
||||
@@ -49,7 +52,10 @@ pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
||||
for name in typed_dicts {
|
||||
let schema = &schemas[&name];
|
||||
let type_desc = schema.get("description").and_then(|d| d.as_str());
|
||||
let props = schema.get("properties").and_then(|p| p.as_object()).unwrap();
|
||||
let props = schema
|
||||
.get("properties")
|
||||
.and_then(|p| p.as_object())
|
||||
.unwrap();
|
||||
|
||||
writeln!(output, "class {}(TypedDict):", name).unwrap();
|
||||
|
||||
@@ -100,9 +106,9 @@ pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
||||
/// Types that reference other types (via $ref) must be defined after their dependencies.
|
||||
fn topological_sort_schemas(schemas: &TypeSchemas) -> Vec<String> {
|
||||
// Build dependency graph
|
||||
let mut deps: HashMap<String, HashSet<String>> = HashMap::new();
|
||||
let mut deps: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
|
||||
for (name, schema) in schemas {
|
||||
let mut type_deps = HashSet::new();
|
||||
let mut type_deps = BTreeSet::new();
|
||||
collect_schema_refs(schema, &mut type_deps);
|
||||
// Only keep deps that are in our schemas
|
||||
type_deps.retain(|d| schemas.contains_key(d));
|
||||
@@ -110,7 +116,7 @@ fn topological_sort_schemas(schemas: &TypeSchemas) -> Vec<String> {
|
||||
}
|
||||
|
||||
// Kahn's algorithm for topological sort
|
||||
let mut in_degree: HashMap<String, usize> = HashMap::new();
|
||||
let mut in_degree: BTreeMap<String, usize> = BTreeMap::new();
|
||||
for name in schemas.keys() {
|
||||
in_degree.insert(name.clone(), 0);
|
||||
}
|
||||
@@ -148,7 +154,7 @@ fn topological_sort_schemas(schemas: &TypeSchemas) -> Vec<String> {
|
||||
result.reverse();
|
||||
|
||||
// Add any types that weren't processed (e.g., due to circular refs or other edge cases)
|
||||
let result_set: HashSet<_> = result.iter().cloned().collect();
|
||||
let result_set: BTreeSet<_> = result.iter().cloned().collect();
|
||||
let mut missing: Vec<_> = schemas
|
||||
.keys()
|
||||
.filter(|k| !result_set.contains(*k))
|
||||
@@ -161,7 +167,7 @@ fn topological_sort_schemas(schemas: &TypeSchemas) -> Vec<String> {
|
||||
}
|
||||
|
||||
/// Collect all type references ($ref) from a schema
|
||||
fn collect_schema_refs(schema: &Value, refs: &mut HashSet<String>) {
|
||||
fn collect_schema_refs(schema: &Value, refs: &mut BTreeSet<String>) {
|
||||
match schema {
|
||||
Value::Object(map) => {
|
||||
if let Some(ref_path) = map.get("$ref").and_then(|r| r.as_str())
|
||||
@@ -215,7 +221,7 @@ fn json_type_to_python(ty: &str, schema: &Value, current_type: Option<&str>) ->
|
||||
pub fn schema_to_python_type(
|
||||
schema: &Value,
|
||||
current_type: Option<&str>,
|
||||
quote_types: Option<&HashSet<String>>,
|
||||
quote_types: Option<&BTreeSet<String>>,
|
||||
) -> String {
|
||||
if let Some(all_of) = schema.get("allOf").and_then(|v| v.as_array()) {
|
||||
for item in all_of {
|
||||
@@ -230,8 +236,8 @@ pub fn schema_to_python_type(
|
||||
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
|
||||
let should_quote = current_type == Some(type_name)
|
||||
|| quote_types.is_some_and(|qt| qt.contains(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);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ pub mod client;
|
||||
pub mod tree;
|
||||
mod types;
|
||||
|
||||
use std::{fmt::Write, fs, io, path::Path};
|
||||
use std::{fmt::Write, io, path::Path};
|
||||
|
||||
use super::write_if_changed;
|
||||
use crate::{ClientMetadata, Endpoint};
|
||||
|
||||
/// Generate Rust client from metadata and OpenAPI endpoints.
|
||||
@@ -38,7 +39,7 @@ pub fn generate_rust_client(
|
||||
tree::generate_tree(&mut output, &metadata.catalog, metadata);
|
||||
api::generate_main_client(&mut output, endpoints);
|
||||
|
||||
fs::write(output_path, output)?;
|
||||
write_if_changed(output_path, &output)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Rust tree structure generation.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt::Write;
|
||||
|
||||
use brk_types::TreeNode;
|
||||
@@ -15,7 +15,7 @@ pub fn generate_tree(output: &mut String, catalog: &TreeNode, metadata: &ClientM
|
||||
writeln!(output, "// Metrics tree\n").unwrap();
|
||||
|
||||
let pattern_lookup = metadata.pattern_lookup();
|
||||
let mut generated = HashSet::new();
|
||||
let mut generated = BTreeSet::new();
|
||||
generate_tree_node(
|
||||
output,
|
||||
"MetricsTree",
|
||||
@@ -32,9 +32,9 @@ fn generate_tree_node(
|
||||
name: &str,
|
||||
path: &str,
|
||||
node: &TreeNode,
|
||||
pattern_lookup: &std::collections::HashMap<Vec<PatternField>, String>,
|
||||
pattern_lookup: &std::collections::BTreeMap<Vec<PatternField>, String>,
|
||||
metadata: &ClientMetadata,
|
||||
generated: &mut HashSet<String>,
|
||||
generated: &mut BTreeSet<String>,
|
||||
) {
|
||||
let Some(ctx) = prepare_tree_node(node, name, path, pattern_lookup, metadata, generated) else {
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Client metadata extracted from brk_query.
|
||||
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use brk_query::Vecs;
|
||||
use brk_types::{Index, MetricLeafWithSchema};
|
||||
@@ -18,11 +18,11 @@ pub struct ClientMetadata {
|
||||
/// Index set patterns - sets of indexes that appear together on metrics
|
||||
pub index_set_patterns: Vec<IndexSetPattern>,
|
||||
/// Maps concrete field signatures to pattern names
|
||||
concrete_to_pattern: HashMap<Vec<PatternField>, String>,
|
||||
concrete_to_pattern: BTreeMap<Vec<PatternField>, String>,
|
||||
/// Maps concrete field signatures to their type parameter (for generic patterns)
|
||||
concrete_to_type_param: HashMap<Vec<PatternField>, String>,
|
||||
concrete_to_type_param: BTreeMap<Vec<PatternField>, String>,
|
||||
/// Maps tree paths to their computed PatternBaseResult
|
||||
node_bases: HashMap<String, PatternBaseResult>,
|
||||
node_bases: BTreeMap<String, PatternBaseResult>,
|
||||
}
|
||||
|
||||
impl ClientMetadata {
|
||||
@@ -134,7 +134,7 @@ impl ClientMetadata {
|
||||
}
|
||||
|
||||
/// Build a lookup map from field signatures to pattern names.
|
||||
pub fn pattern_lookup(&self) -> HashMap<Vec<PatternField>, String> {
|
||||
pub fn pattern_lookup(&self) -> BTreeMap<Vec<PatternField>, String> {
|
||||
let mut lookup = self.concrete_to_pattern.clone();
|
||||
for p in &self.structural_patterns {
|
||||
lookup.insert(p.fields.clone(), p.name.clone());
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! - Suffix mode: `_m(acc, relative)` → `acc_relative` or just `relative` if acc empty
|
||||
//! - Prefix mode: `_p(prefix, acc)` → `prefix_acc` or just `acc` if prefix empty
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// How a pattern constructs metric names from the accumulator.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -14,13 +14,13 @@ pub enum PatternMode {
|
||||
/// Example: `_m("lth", "max_cost_basis")` → `"lth_max_cost_basis"`
|
||||
Suffix {
|
||||
/// Maps field name to its relative name (full metric name when acc = "")
|
||||
relatives: HashMap<String, String>,
|
||||
relatives: BTreeMap<String, String>,
|
||||
},
|
||||
/// Fields prepend their prefix to acc.
|
||||
/// Formula: `_p(prefix, acc)` → `{prefix}_{acc}` or `{acc}` if prefix empty
|
||||
/// Example: `_p("cumulative", "lth_realized_loss")` → `"cumulative_lth_realized_loss"`
|
||||
Prefix {
|
||||
/// Maps field name to its prefix (empty string for identity)
|
||||
prefixes: HashMap<String, String>,
|
||||
prefixes: BTreeMap<String, String>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Structural pattern and field types.
|
||||
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use brk_types::Index;
|
||||
|
||||
@@ -37,7 +37,9 @@ impl StructuralPattern {
|
||||
/// Get the field part (relative name or prefix) for a given field.
|
||||
pub fn get_field_part(&self, field_name: &str) -> Option<&str> {
|
||||
match &self.mode {
|
||||
Some(PatternMode::Suffix { relatives }) => relatives.get(field_name).map(|s| s.as_str()),
|
||||
Some(PatternMode::Suffix { relatives }) => {
|
||||
relatives.get(field_name).map(|s| s.as_str())
|
||||
}
|
||||
Some(PatternMode::Prefix { prefixes }) => prefixes.get(field_name).map(|s| s.as_str()),
|
||||
None => None,
|
||||
}
|
||||
@@ -50,7 +52,7 @@ impl StructuralPattern {
|
||||
|
||||
/// Check if the given instance field parts match this pattern's field parts.
|
||||
/// Returns true if all field parts in the pattern match the instance's field parts.
|
||||
pub fn field_parts_match(&self, instance_field_parts: &HashMap<String, String>) -> bool {
|
||||
pub fn field_parts_match(&self, instance_field_parts: &BTreeMap<String, String>) -> bool {
|
||||
match &self.mode {
|
||||
Some(PatternMode::Suffix { relatives }) => {
|
||||
// For each field in the pattern, check if the instance has the same suffix
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Tests that verify pattern analysis using the real catalog.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt::Write;
|
||||
|
||||
use brk_bindgen::ClientMetadata;
|
||||
@@ -26,7 +26,7 @@ fn load_metadata() -> ClientMetadata {
|
||||
}
|
||||
|
||||
/// Collect all leaf metric names from a tree.
|
||||
fn collect_leaf_names(node: &TreeNode, names: &mut HashSet<String>) {
|
||||
fn collect_leaf_names(node: &TreeNode, names: &mut BTreeSet<String>) {
|
||||
match node {
|
||||
TreeNode::Leaf(leaf) => {
|
||||
names.insert(leaf.name().to_string());
|
||||
@@ -63,7 +63,7 @@ fn test_catalog_loads() {
|
||||
#[test]
|
||||
fn test_all_leaves_have_names() {
|
||||
let catalog = load_catalog();
|
||||
let mut names = HashSet::new();
|
||||
let mut names = BTreeSet::new();
|
||||
collect_leaf_names(&catalog, &mut names);
|
||||
|
||||
println!("Catalog has {} unique metric names", names.len());
|
||||
@@ -246,11 +246,11 @@ fn test_parameterizable_patterns_have_mode() {
|
||||
// Verify all parameterizable patterns have valid modes with all fields
|
||||
for pattern in ¶meterizable {
|
||||
let mode = pattern.mode.as_ref().unwrap();
|
||||
let field_names: HashSet<_> = pattern.fields.iter().map(|f| f.name.clone()).collect();
|
||||
let field_names: BTreeSet<_> = pattern.fields.iter().map(|f| f.name.clone()).collect();
|
||||
|
||||
match mode {
|
||||
brk_bindgen::PatternMode::Suffix { relatives } => {
|
||||
let mode_fields: HashSet<_> = relatives.keys().cloned().collect();
|
||||
let mode_fields: BTreeSet<_> = relatives.keys().cloned().collect();
|
||||
assert_eq!(
|
||||
field_names, mode_fields,
|
||||
"Pattern {} suffix mode should have all fields",
|
||||
@@ -258,7 +258,7 @@ fn test_parameterizable_patterns_have_mode() {
|
||||
);
|
||||
}
|
||||
brk_bindgen::PatternMode::Prefix { prefixes } => {
|
||||
let mode_fields: HashSet<_> = prefixes.keys().cloned().collect();
|
||||
let mode_fields: BTreeSet<_> = prefixes.keys().cloned().collect();
|
||||
assert_eq!(
|
||||
field_names, mode_fields,
|
||||
"Pattern {} prefix mode should have all fields",
|
||||
@@ -333,7 +333,7 @@ fn test_generated_rust_output() {
|
||||
let metadata = ClientMetadata::from_catalog(catalog.clone());
|
||||
|
||||
// Collect all metric names from the catalog
|
||||
let mut all_metrics = HashSet::new();
|
||||
let mut all_metrics = BTreeSet::new();
|
||||
collect_leaf_names(&catalog, &mut all_metrics);
|
||||
|
||||
// Generate Rust client output
|
||||
@@ -416,7 +416,7 @@ fn test_generated_javascript_output() {
|
||||
let metadata = ClientMetadata::from_catalog(catalog.clone());
|
||||
|
||||
// Collect all metric names from the catalog
|
||||
let mut all_metrics = HashSet::new();
|
||||
let mut all_metrics = BTreeSet::new();
|
||||
collect_leaf_names(&catalog, &mut all_metrics);
|
||||
|
||||
// Load schemas from OpenAPI spec only (catalog schemas require runtime data)
|
||||
@@ -507,7 +507,7 @@ fn test_generated_python_output() {
|
||||
let metadata = ClientMetadata::from_catalog(catalog.clone());
|
||||
|
||||
// Collect all metric names from the catalog
|
||||
let mut all_metrics = HashSet::new();
|
||||
let mut all_metrics = BTreeSet::new();
|
||||
collect_leaf_names(&catalog, &mut all_metrics);
|
||||
|
||||
// Load schemas from OpenAPI spec only (catalog schemas require runtime data)
|
||||
@@ -665,8 +665,8 @@ fn test_cost_basis_relatives() {
|
||||
|
||||
// For leaves (max, min), the base is the metric name
|
||||
// For branches (percentiles), the base is the common prefix of its children
|
||||
let mut child_bases: std::collections::HashMap<String, String> =
|
||||
std::collections::HashMap::new();
|
||||
let mut child_bases: std::collections::BTreeMap<String, String> =
|
||||
std::collections::BTreeMap::new();
|
||||
for (field, metric) in metrics {
|
||||
if field.starts_with("percentiles.") {
|
||||
// This is a percentile metric - compute what the percentiles branch would return
|
||||
@@ -721,20 +721,20 @@ fn test_debug_cost_basis_pattern2_mode() {
|
||||
#[derive(Debug, Clone)]
|
||||
struct DebugInstanceAnalysis {
|
||||
base: String,
|
||||
field_parts: std::collections::HashMap<String, String>,
|
||||
field_parts: std::collections::BTreeMap<String, String>,
|
||||
is_suffix_mode: bool,
|
||||
}
|
||||
|
||||
fn collect_debug(
|
||||
node: &TreeNode,
|
||||
pattern_lookup: &std::collections::HashMap<Vec<brk_bindgen::PatternField>, String>,
|
||||
all_analyses: &mut std::collections::HashMap<String, Vec<DebugInstanceAnalysis>>,
|
||||
pattern_lookup: &std::collections::BTreeMap<Vec<brk_bindgen::PatternField>, String>,
|
||||
all_analyses: &mut std::collections::BTreeMap<String, Vec<DebugInstanceAnalysis>>,
|
||||
) -> Option<String> {
|
||||
match node {
|
||||
TreeNode::Leaf(leaf) => Some(leaf.name().to_string()),
|
||||
TreeNode::Branch(children) => {
|
||||
let mut child_bases: std::collections::HashMap<String, String> =
|
||||
std::collections::HashMap::new();
|
||||
let mut child_bases: std::collections::BTreeMap<String, String> =
|
||||
std::collections::BTreeMap::new();
|
||||
for (field_name, child_node) in children {
|
||||
if let Some(base) = collect_debug(child_node, pattern_lookup, all_analyses) {
|
||||
child_bases.insert(field_name.clone(), base);
|
||||
@@ -750,7 +750,7 @@ fn test_debug_cost_basis_pattern2_mode() {
|
||||
let (base, field_parts, is_suffix_mode) =
|
||||
if let Some(common_prefix) = brk_bindgen::find_common_prefix(&bases) {
|
||||
let base = common_prefix.trim_end_matches('_').to_string();
|
||||
let mut parts = std::collections::HashMap::new();
|
||||
let mut parts = std::collections::BTreeMap::new();
|
||||
for (field_name, child_base) in &child_bases {
|
||||
let relative = if *child_base == base {
|
||||
String::new()
|
||||
@@ -792,8 +792,7 @@ fn test_debug_cost_basis_pattern2_mode() {
|
||||
}
|
||||
}
|
||||
|
||||
let mut all_analyses: std::collections::HashMap<String, Vec<DebugInstanceAnalysis>> =
|
||||
std::collections::HashMap::new();
|
||||
let mut all_analyses: BTreeMap<String, Vec<DebugInstanceAnalysis>> = BTreeMap::new();
|
||||
collect_debug(&catalog, &pattern_lookup, &mut all_analyses);
|
||||
|
||||
if let Some(analyses) = all_analyses.get("CostBasisPattern2") {
|
||||
|
||||
+230
-230
@@ -1535,18 +1535,18 @@ impl<T: DeserializeOwned> ClassDaysInLossPattern<T> {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
_2015: MetricPattern4::new(client.clone(), _m(&acc, "2015_days_in_profit")),
|
||||
_2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_days_in_profit")),
|
||||
_2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_days_in_profit")),
|
||||
_2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_days_in_profit")),
|
||||
_2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_days_in_profit")),
|
||||
_2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_days_in_profit")),
|
||||
_2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_days_in_profit")),
|
||||
_2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_days_in_profit")),
|
||||
_2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_days_in_profit")),
|
||||
_2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_days_in_profit")),
|
||||
_2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_days_in_profit")),
|
||||
_2026: MetricPattern4::new(client.clone(), _m(&acc, "2026_days_in_profit")),
|
||||
_2015: MetricPattern4::new(client.clone(), _m(&acc, "2015_returns")),
|
||||
_2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_returns")),
|
||||
_2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_returns")),
|
||||
_2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_returns")),
|
||||
_2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_returns")),
|
||||
_2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_returns")),
|
||||
_2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_returns")),
|
||||
_2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_returns")),
|
||||
_2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_returns")),
|
||||
_2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_returns")),
|
||||
_2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_returns")),
|
||||
_2026: MetricPattern4::new(client.clone(), _m(&acc, "2026_returns")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1619,38 +1619,6 @@ impl<T: DeserializeOwned> DollarsPattern<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RelativePattern2 {
|
||||
pub neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1<StoredF32>,
|
||||
pub neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1<StoredF32>,
|
||||
pub net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1<StoredF32>,
|
||||
pub net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1<StoredF32>,
|
||||
pub supply_in_loss_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_in_profit_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub unrealized_loss_rel_to_own_market_cap: MetricPattern1<StoredF32>,
|
||||
pub unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1<StoredF32>,
|
||||
pub unrealized_profit_rel_to_own_market_cap: MetricPattern1<StoredF32>,
|
||||
pub unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1<StoredF32>,
|
||||
}
|
||||
|
||||
impl RelativePattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_own_market_cap")),
|
||||
neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_own_total_unrealized_pnl")),
|
||||
net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_own_market_cap")),
|
||||
net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_own_total_unrealized_pnl")),
|
||||
supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_own_supply")),
|
||||
supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_own_supply")),
|
||||
unrealized_loss_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_own_market_cap")),
|
||||
unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_own_total_unrealized_pnl")),
|
||||
unrealized_profit_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_own_market_cap")),
|
||||
unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_own_total_unrealized_pnl")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RelativePattern {
|
||||
pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
|
||||
@@ -1683,6 +1651,38 @@ impl RelativePattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RelativePattern2 {
|
||||
pub neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1<StoredF32>,
|
||||
pub neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1<StoredF32>,
|
||||
pub net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1<StoredF32>,
|
||||
pub net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1<StoredF32>,
|
||||
pub supply_in_loss_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_in_profit_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub unrealized_loss_rel_to_own_market_cap: MetricPattern1<StoredF32>,
|
||||
pub unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1<StoredF32>,
|
||||
pub unrealized_profit_rel_to_own_market_cap: MetricPattern1<StoredF32>,
|
||||
pub unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1<StoredF32>,
|
||||
}
|
||||
|
||||
impl RelativePattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_own_market_cap")),
|
||||
neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_own_total_unrealized_pnl")),
|
||||
net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_own_market_cap")),
|
||||
net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_own_total_unrealized_pnl")),
|
||||
supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_own_supply")),
|
||||
supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_own_supply")),
|
||||
unrealized_loss_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_own_market_cap")),
|
||||
unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_own_total_unrealized_pnl")),
|
||||
unrealized_profit_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_own_market_cap")),
|
||||
unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_own_total_unrealized_pnl")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CountPattern2<T> {
|
||||
pub average: MetricPattern1<T>,
|
||||
@@ -1833,6 +1833,32 @@ impl _0satsPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct PeriodCagrPattern {
|
||||
pub _10y: MetricPattern4<StoredF32>,
|
||||
pub _2y: MetricPattern4<StoredF32>,
|
||||
pub _3y: MetricPattern4<StoredF32>,
|
||||
pub _4y: MetricPattern4<StoredF32>,
|
||||
pub _5y: MetricPattern4<StoredF32>,
|
||||
pub _6y: MetricPattern4<StoredF32>,
|
||||
pub _8y: MetricPattern4<StoredF32>,
|
||||
}
|
||||
|
||||
impl PeriodCagrPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
_10y: MetricPattern4::new(client.clone(), _p("10y", &acc)),
|
||||
_2y: MetricPattern4::new(client.clone(), _p("2y", &acc)),
|
||||
_3y: MetricPattern4::new(client.clone(), _p("3y", &acc)),
|
||||
_4y: MetricPattern4::new(client.clone(), _p("4y", &acc)),
|
||||
_5y: MetricPattern4::new(client.clone(), _p("5y", &acc)),
|
||||
_6y: MetricPattern4::new(client.clone(), _p("6y", &acc)),
|
||||
_8y: MetricPattern4::new(client.clone(), _p("8y", &acc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _100btcPattern {
|
||||
pub activity: ActivityPattern2,
|
||||
@@ -1911,58 +1937,6 @@ impl _10yPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct UnrealizedPattern {
|
||||
pub neg_unrealized_loss: MetricPattern1<Dollars>,
|
||||
pub net_unrealized_pnl: MetricPattern1<Dollars>,
|
||||
pub supply_in_loss: ActiveSupplyPattern,
|
||||
pub supply_in_profit: ActiveSupplyPattern,
|
||||
pub total_unrealized_pnl: MetricPattern1<Dollars>,
|
||||
pub unrealized_loss: MetricPattern1<Dollars>,
|
||||
pub unrealized_profit: MetricPattern1<Dollars>,
|
||||
}
|
||||
|
||||
impl UnrealizedPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
neg_unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss")),
|
||||
net_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl")),
|
||||
supply_in_loss: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_loss")),
|
||||
supply_in_profit: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_profit")),
|
||||
total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "total_unrealized_pnl")),
|
||||
unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss")),
|
||||
unrealized_profit: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct PeriodCagrPattern {
|
||||
pub _10y: MetricPattern4<StoredF32>,
|
||||
pub _2y: MetricPattern4<StoredF32>,
|
||||
pub _3y: MetricPattern4<StoredF32>,
|
||||
pub _4y: MetricPattern4<StoredF32>,
|
||||
pub _5y: MetricPattern4<StoredF32>,
|
||||
pub _6y: MetricPattern4<StoredF32>,
|
||||
pub _8y: MetricPattern4<StoredF32>,
|
||||
}
|
||||
|
||||
impl PeriodCagrPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
_10y: MetricPattern4::new(client.clone(), _p("10y", &acc)),
|
||||
_2y: MetricPattern4::new(client.clone(), _p("2y", &acc)),
|
||||
_3y: MetricPattern4::new(client.clone(), _p("3y", &acc)),
|
||||
_4y: MetricPattern4::new(client.clone(), _p("4y", &acc)),
|
||||
_5y: MetricPattern4::new(client.clone(), _p("5y", &acc)),
|
||||
_6y: MetricPattern4::new(client.clone(), _p("6y", &acc)),
|
||||
_8y: MetricPattern4::new(client.clone(), _p("8y", &acc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _10yTo12yPattern {
|
||||
pub activity: ActivityPattern2,
|
||||
@@ -1989,6 +1963,32 @@ impl _10yTo12yPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct UnrealizedPattern {
|
||||
pub neg_unrealized_loss: MetricPattern1<Dollars>,
|
||||
pub net_unrealized_pnl: MetricPattern1<Dollars>,
|
||||
pub supply_in_loss: ActiveSupplyPattern,
|
||||
pub supply_in_profit: ActiveSupplyPattern,
|
||||
pub total_unrealized_pnl: MetricPattern1<Dollars>,
|
||||
pub unrealized_loss: MetricPattern1<Dollars>,
|
||||
pub unrealized_profit: MetricPattern1<Dollars>,
|
||||
}
|
||||
|
||||
impl UnrealizedPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
neg_unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss")),
|
||||
net_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl")),
|
||||
supply_in_loss: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_loss")),
|
||||
supply_in_profit: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_profit")),
|
||||
total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "total_unrealized_pnl")),
|
||||
unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss")),
|
||||
unrealized_profit: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AllPattern {
|
||||
pub balance_decreased: FullnessPattern<StoredU32>,
|
||||
@@ -2056,37 +2056,19 @@ impl<T: DeserializeOwned> SplitPattern2<T> {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct UnclaimedRewardsPattern {
|
||||
pub bitcoin: BitcoinPattern2<Bitcoin>,
|
||||
pub dollars: BlockCountPattern<Dollars>,
|
||||
pub sats: BlockCountPattern<Sats>,
|
||||
pub struct SegwitAdoptionPattern {
|
||||
pub base: MetricPattern11<StoredF32>,
|
||||
pub cumulative: MetricPattern2<StoredF32>,
|
||||
pub sum: MetricPattern2<StoredF32>,
|
||||
}
|
||||
|
||||
impl UnclaimedRewardsPattern {
|
||||
impl SegwitAdoptionPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
bitcoin: BitcoinPattern2::new(client.clone(), _m(&acc, "btc")),
|
||||
dollars: BlockCountPattern::new(client.clone(), _m(&acc, "usd")),
|
||||
sats: BlockCountPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _2015Pattern {
|
||||
pub bitcoin: MetricPattern4<Bitcoin>,
|
||||
pub dollars: MetricPattern4<Dollars>,
|
||||
pub sats: MetricPattern4<Sats>,
|
||||
}
|
||||
|
||||
impl _2015Pattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
bitcoin: MetricPattern4::new(client.clone(), _m(&acc, "btc")),
|
||||
dollars: MetricPattern4::new(client.clone(), _m(&acc, "usd")),
|
||||
sats: MetricPattern4::new(client.clone(), acc.clone()),
|
||||
base: MetricPattern11::new(client.clone(), acc.clone()),
|
||||
cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")),
|
||||
sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2109,6 +2091,24 @@ impl ActiveSupplyPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _2015Pattern {
|
||||
pub bitcoin: MetricPattern4<Bitcoin>,
|
||||
pub dollars: MetricPattern4<Dollars>,
|
||||
pub sats: MetricPattern4<Sats>,
|
||||
}
|
||||
|
||||
impl _2015Pattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
bitcoin: MetricPattern4::new(client.clone(), _m(&acc, "btc")),
|
||||
dollars: MetricPattern4::new(client.clone(), _m(&acc, "usd")),
|
||||
sats: MetricPattern4::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CoinbasePattern {
|
||||
pub bitcoin: BitcoinPattern,
|
||||
@@ -2128,37 +2128,19 @@ impl CoinbasePattern {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct SegwitAdoptionPattern {
|
||||
pub base: MetricPattern11<StoredF32>,
|
||||
pub cumulative: MetricPattern2<StoredF32>,
|
||||
pub sum: MetricPattern2<StoredF32>,
|
||||
pub struct UnclaimedRewardsPattern {
|
||||
pub bitcoin: BitcoinPattern2<Bitcoin>,
|
||||
pub dollars: BlockCountPattern<Dollars>,
|
||||
pub sats: BlockCountPattern<Sats>,
|
||||
}
|
||||
|
||||
impl SegwitAdoptionPattern {
|
||||
impl UnclaimedRewardsPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
base: MetricPattern11::new(client.clone(), acc.clone()),
|
||||
cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")),
|
||||
sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CostBasisPattern2 {
|
||||
pub max: ActivePricePattern,
|
||||
pub min: ActivePricePattern,
|
||||
pub percentiles: PercentilesPattern,
|
||||
}
|
||||
|
||||
impl CostBasisPattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
max: ActivePricePattern::new(client.clone(), _m(&acc, "max_cost_basis")),
|
||||
min: ActivePricePattern::new(client.clone(), _m(&acc, "min_cost_basis")),
|
||||
percentiles: PercentilesPattern::new(client.clone(), _m(&acc, "cost_basis")),
|
||||
bitcoin: BitcoinPattern2::new(client.clone(), _m(&acc, "btc")),
|
||||
dollars: BlockCountPattern::new(client.clone(), _m(&acc, "usd")),
|
||||
sats: BlockCountPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2182,17 +2164,19 @@ impl CoinbasePattern2 {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RelativePattern4 {
|
||||
pub supply_in_loss_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_in_profit_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub struct CostBasisPattern2 {
|
||||
pub max: ActivePricePattern,
|
||||
pub min: ActivePricePattern,
|
||||
pub percentiles: PercentilesPattern,
|
||||
}
|
||||
|
||||
impl RelativePattern4 {
|
||||
impl CostBasisPattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "loss_rel_to_own_supply")),
|
||||
supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "profit_rel_to_own_supply")),
|
||||
max: ActivePricePattern::new(client.clone(), _m(&acc, "max_cost_basis")),
|
||||
min: ActivePricePattern::new(client.clone(), _m(&acc, "min_cost_basis")),
|
||||
percentiles: PercentilesPattern::new(client.clone(), _m(&acc, "cost_basis")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2214,17 +2198,17 @@ impl ActivePricePattern {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _1dReturns1mSdPattern {
|
||||
pub sd: MetricPattern4<StoredF32>,
|
||||
pub sma: MetricPattern4<StoredF32>,
|
||||
pub struct _0sdUsdPattern {
|
||||
pub dollars: MetricPattern4<Dollars>,
|
||||
pub sats: MetricPattern4<SatsFract>,
|
||||
}
|
||||
|
||||
impl _1dReturns1mSdPattern {
|
||||
impl _0sdUsdPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
sd: MetricPattern4::new(client.clone(), _m(&acc, "sd")),
|
||||
sma: MetricPattern4::new(client.clone(), _m(&acc, "sma")),
|
||||
dollars: MetricPattern4::new(client.clone(), acc.clone()),
|
||||
sats: MetricPattern4::new(client.clone(), _m(&acc, "sats")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2245,22 +2229,6 @@ impl SupplyPattern2 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _0sdUsdPattern {
|
||||
pub dollars: MetricPattern4<Dollars>,
|
||||
pub sats: MetricPattern4<SatsFract>,
|
||||
}
|
||||
|
||||
impl _0sdUsdPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
dollars: MetricPattern4::new(client.clone(), acc.clone()),
|
||||
sats: MetricPattern4::new(client.clone(), _m(&acc, "sats")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CostBasisPattern {
|
||||
pub max: ActivePricePattern,
|
||||
@@ -2277,6 +2245,38 @@ impl CostBasisPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _1dReturns1mSdPattern {
|
||||
pub sd: MetricPattern4<StoredF32>,
|
||||
pub sma: MetricPattern4<StoredF32>,
|
||||
}
|
||||
|
||||
impl _1dReturns1mSdPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
sd: MetricPattern4::new(client.clone(), _m(&acc, "sd")),
|
||||
sma: MetricPattern4::new(client.clone(), _m(&acc, "sma")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RelativePattern4 {
|
||||
pub supply_in_loss_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_in_profit_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
}
|
||||
|
||||
impl RelativePattern4 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "loss_rel_to_own_supply")),
|
||||
supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "profit_rel_to_own_supply")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct BlockCountPattern<T> {
|
||||
pub cumulative: MetricPattern1<T>,
|
||||
@@ -2325,20 +2325,6 @@ impl<T: DeserializeOwned> SatsPattern<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct OutputsPattern {
|
||||
pub utxo_count: MetricPattern1<StoredU64>,
|
||||
}
|
||||
|
||||
impl OutputsPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
utxo_count: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RealizedPriceExtraPattern {
|
||||
pub ratio: MetricPattern4<StoredF32>,
|
||||
@@ -2353,6 +2339,20 @@ impl RealizedPriceExtraPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct OutputsPattern {
|
||||
pub utxo_count: MetricPattern1<StoredU64>,
|
||||
}
|
||||
|
||||
impl OutputsPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
utxo_count: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics tree
|
||||
|
||||
/// Metrics tree node.
|
||||
@@ -4276,10 +4276,10 @@ impl MetricsTree_Market_Ath {
|
||||
pub struct MetricsTree_Market_Dca {
|
||||
pub class_average_price: MetricsTree_Market_Dca_ClassAveragePrice,
|
||||
pub class_days_in_loss: MetricsTree_Market_Dca_ClassDaysInLoss,
|
||||
pub class_days_in_profit: ClassDaysInLossPattern<StoredU32>,
|
||||
pub class_days_in_profit: MetricsTree_Market_Dca_ClassDaysInProfit,
|
||||
pub class_max_drawdown: MetricsTree_Market_Dca_ClassMaxDrawdown,
|
||||
pub class_max_return: MetricsTree_Market_Dca_ClassMaxReturn,
|
||||
pub class_returns: MetricsTree_Market_Dca_ClassReturns,
|
||||
pub class_returns: ClassDaysInLossPattern<StoredF32>,
|
||||
pub class_stack: MetricsTree_Market_Dca_ClassStack,
|
||||
pub period_average_price: MetricsTree_Market_Dca_PeriodAveragePrice,
|
||||
pub period_cagr: PeriodCagrPattern,
|
||||
@@ -4302,10 +4302,10 @@ impl MetricsTree_Market_Dca {
|
||||
Self {
|
||||
class_average_price: MetricsTree_Market_Dca_ClassAveragePrice::new(client.clone(), format!("{base_path}_class_average_price")),
|
||||
class_days_in_loss: MetricsTree_Market_Dca_ClassDaysInLoss::new(client.clone(), format!("{base_path}_class_days_in_loss")),
|
||||
class_days_in_profit: ClassDaysInLossPattern::new(client.clone(), "dca_class".to_string()),
|
||||
class_days_in_profit: MetricsTree_Market_Dca_ClassDaysInProfit::new(client.clone(), format!("{base_path}_class_days_in_profit")),
|
||||
class_max_drawdown: MetricsTree_Market_Dca_ClassMaxDrawdown::new(client.clone(), format!("{base_path}_class_max_drawdown")),
|
||||
class_max_return: MetricsTree_Market_Dca_ClassMaxReturn::new(client.clone(), format!("{base_path}_class_max_return")),
|
||||
class_returns: MetricsTree_Market_Dca_ClassReturns::new(client.clone(), format!("{base_path}_class_returns")),
|
||||
class_returns: ClassDaysInLossPattern::new(client.clone(), "dca_class".to_string()),
|
||||
class_stack: MetricsTree_Market_Dca_ClassStack::new(client.clone(), format!("{base_path}_class_stack")),
|
||||
period_average_price: MetricsTree_Market_Dca_PeriodAveragePrice::new(client.clone(), format!("{base_path}_period_average_price")),
|
||||
period_cagr: PeriodCagrPattern::new(client.clone(), "dca_cagr".to_string()),
|
||||
@@ -4395,6 +4395,41 @@ impl MetricsTree_Market_Dca_ClassDaysInLoss {
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Market_Dca_ClassDaysInProfit {
|
||||
pub _2015: MetricPattern4<StoredU32>,
|
||||
pub _2016: MetricPattern4<StoredU32>,
|
||||
pub _2017: MetricPattern4<StoredU32>,
|
||||
pub _2018: MetricPattern4<StoredU32>,
|
||||
pub _2019: MetricPattern4<StoredU32>,
|
||||
pub _2020: MetricPattern4<StoredU32>,
|
||||
pub _2021: MetricPattern4<StoredU32>,
|
||||
pub _2022: MetricPattern4<StoredU32>,
|
||||
pub _2023: MetricPattern4<StoredU32>,
|
||||
pub _2024: MetricPattern4<StoredU32>,
|
||||
pub _2025: MetricPattern4<StoredU32>,
|
||||
pub _2026: MetricPattern4<StoredU32>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Market_Dca_ClassDaysInProfit {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
_2015: MetricPattern4::new(client.clone(), "dca_class_2015_days_in_profit".to_string()),
|
||||
_2016: MetricPattern4::new(client.clone(), "dca_class_2016_days_in_profit".to_string()),
|
||||
_2017: MetricPattern4::new(client.clone(), "dca_class_2017_days_in_profit".to_string()),
|
||||
_2018: MetricPattern4::new(client.clone(), "dca_class_2018_days_in_profit".to_string()),
|
||||
_2019: MetricPattern4::new(client.clone(), "dca_class_2019_days_in_profit".to_string()),
|
||||
_2020: MetricPattern4::new(client.clone(), "dca_class_2020_days_in_profit".to_string()),
|
||||
_2021: MetricPattern4::new(client.clone(), "dca_class_2021_days_in_profit".to_string()),
|
||||
_2022: MetricPattern4::new(client.clone(), "dca_class_2022_days_in_profit".to_string()),
|
||||
_2023: MetricPattern4::new(client.clone(), "dca_class_2023_days_in_profit".to_string()),
|
||||
_2024: MetricPattern4::new(client.clone(), "dca_class_2024_days_in_profit".to_string()),
|
||||
_2025: MetricPattern4::new(client.clone(), "dca_class_2025_days_in_profit".to_string()),
|
||||
_2026: MetricPattern4::new(client.clone(), "dca_class_2026_days_in_profit".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Market_Dca_ClassMaxDrawdown {
|
||||
pub _2015: MetricPattern4<StoredF32>,
|
||||
@@ -4465,41 +4500,6 @@ impl MetricsTree_Market_Dca_ClassMaxReturn {
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Market_Dca_ClassReturns {
|
||||
pub _2015: MetricPattern4<StoredF32>,
|
||||
pub _2016: MetricPattern4<StoredF32>,
|
||||
pub _2017: MetricPattern4<StoredF32>,
|
||||
pub _2018: MetricPattern4<StoredF32>,
|
||||
pub _2019: MetricPattern4<StoredF32>,
|
||||
pub _2020: MetricPattern4<StoredF32>,
|
||||
pub _2021: MetricPattern4<StoredF32>,
|
||||
pub _2022: MetricPattern4<StoredF32>,
|
||||
pub _2023: MetricPattern4<StoredF32>,
|
||||
pub _2024: MetricPattern4<StoredF32>,
|
||||
pub _2025: MetricPattern4<StoredF32>,
|
||||
pub _2026: MetricPattern4<StoredF32>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Market_Dca_ClassReturns {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
_2015: MetricPattern4::new(client.clone(), "dca_class_2015_returns".to_string()),
|
||||
_2016: MetricPattern4::new(client.clone(), "dca_class_2016_returns".to_string()),
|
||||
_2017: MetricPattern4::new(client.clone(), "dca_class_2017_returns".to_string()),
|
||||
_2018: MetricPattern4::new(client.clone(), "dca_class_2018_returns".to_string()),
|
||||
_2019: MetricPattern4::new(client.clone(), "dca_class_2019_returns".to_string()),
|
||||
_2020: MetricPattern4::new(client.clone(), "dca_class_2020_returns".to_string()),
|
||||
_2021: MetricPattern4::new(client.clone(), "dca_class_2021_returns".to_string()),
|
||||
_2022: MetricPattern4::new(client.clone(), "dca_class_2022_returns".to_string()),
|
||||
_2023: MetricPattern4::new(client.clone(), "dca_class_2023_returns".to_string()),
|
||||
_2024: MetricPattern4::new(client.clone(), "dca_class_2024_returns".to_string()),
|
||||
_2025: MetricPattern4::new(client.clone(), "dca_class_2025_returns".to_string()),
|
||||
_2026: MetricPattern4::new(client.clone(), "dca_class_2026_returns".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Market_Dca_ClassStack {
|
||||
pub _2015: _2015Pattern,
|
||||
@@ -5651,7 +5651,7 @@ pub struct BrkClient {
|
||||
|
||||
impl BrkClient {
|
||||
/// Client version.
|
||||
pub const VERSION: &'static str = "v0.1.0";
|
||||
pub const VERSION: &'static str = "v0.1.1";
|
||||
|
||||
/// Create a new client with the given base URL.
|
||||
pub fn new(base_url: impl Into<String>) -> Self {
|
||||
|
||||
@@ -7,7 +7,7 @@ use brk_iterator::Blocks;
|
||||
use brk_rpc::Client;
|
||||
use brk_types::Height;
|
||||
use tracing::{debug, info};
|
||||
use vecdb::Exit;
|
||||
use vecdb::{Exit, TypedVecIterator};
|
||||
mod constants;
|
||||
mod indexes;
|
||||
mod processor;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::{fs, path::Path, time::Instant};
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_store::{AnyStore, Kind, Mode, Store};
|
||||
@@ -228,6 +230,7 @@ impl Stores {
|
||||
});
|
||||
|
||||
// Remove address hashes for all address types starting from rollback height
|
||||
// (each address only appears once in bytes vec, so no dedup needed)
|
||||
for address_type in [
|
||||
OutputType::P2PK65,
|
||||
OutputType::P2PK33,
|
||||
@@ -280,6 +283,11 @@ impl Stores {
|
||||
let mut txoutindex_to_outputtype_iter = vecs.outputs.outputtype.iter()?;
|
||||
let mut txoutindex_to_typeindex_iter = vecs.outputs.typeindex.iter()?;
|
||||
|
||||
// Collect unique (addresstype, addressindex, txindex) to avoid double deletion
|
||||
// when same address receives multiple outputs in same transaction
|
||||
let mut addressindex_txindex_to_remove: FxHashSet<(OutputType, TypeIndex, TxIndex)> =
|
||||
FxHashSet::default();
|
||||
|
||||
for txoutindex in
|
||||
starting_indexes.txoutindex.to_usize()..vecs.outputs.outputtype.len()
|
||||
{
|
||||
@@ -292,9 +300,7 @@ impl Stores {
|
||||
let addressindex = txoutindex_to_typeindex_iter.get_at_unwrap(txoutindex);
|
||||
let txindex = txoutindex_to_txindex_iter.get_at_unwrap(txoutindex);
|
||||
|
||||
self.addresstype_to_addressindex_and_txindex
|
||||
.get_mut_unwrap(addresstype)
|
||||
.remove(AddressIndexTxIndex::from((addressindex, txindex)));
|
||||
addressindex_txindex_to_remove.insert((addresstype, addressindex, txindex));
|
||||
|
||||
let vout = Vout::from(
|
||||
txoutindex
|
||||
@@ -304,11 +310,14 @@ impl Stores {
|
||||
);
|
||||
let outpoint = OutPoint::new(txindex, vout);
|
||||
|
||||
// OutPoints are unique per output, no dedup needed
|
||||
self.addresstype_to_addressindex_and_unspentoutpoint
|
||||
.get_mut_unwrap(addresstype)
|
||||
.remove(AddressIndexOutPoint::from((addressindex, outpoint)));
|
||||
}
|
||||
|
||||
// Don't remove yet - merge with second loop's set first
|
||||
|
||||
// Collect outputs that were spent after the rollback point
|
||||
// We need to: 1) reset their spend status, 2) restore address stores
|
||||
let mut txindex_to_first_txoutindex_iter =
|
||||
@@ -349,21 +358,30 @@ impl Stores {
|
||||
.collect();
|
||||
|
||||
// Now process the collected outputs (iterators dropped, can mutate vecs)
|
||||
// Add spending tx entries to the same set (avoid double deletion when same tx
|
||||
// both creates output to address A and spends output from address A)
|
||||
for (outpoint, outputtype, typeindex, spending_txindex) in outputs_to_unspend {
|
||||
// Restore address stores if this is an address output
|
||||
if outputtype.is_address() {
|
||||
let addresstype = outputtype;
|
||||
let addressindex = typeindex;
|
||||
|
||||
self.addresstype_to_addressindex_and_txindex
|
||||
.get_mut_unwrap(addresstype)
|
||||
.remove(AddressIndexTxIndex::from((addressindex, spending_txindex)));
|
||||
// Add to same set as first loop
|
||||
addressindex_txindex_to_remove.insert((addresstype, addressindex, spending_txindex));
|
||||
|
||||
// OutPoints are unique, no dedup needed for insert
|
||||
self.addresstype_to_addressindex_and_unspentoutpoint
|
||||
.get_mut_unwrap(addresstype)
|
||||
.insert(AddressIndexOutPoint::from((addressindex, outpoint)), Unit);
|
||||
}
|
||||
}
|
||||
|
||||
// Now remove all deduplicated addressindex_txindex entries (from both loops)
|
||||
for (addresstype, addressindex, txindex) in addressindex_txindex_to_remove {
|
||||
self.addresstype_to_addressindex_and_txindex
|
||||
.get_mut_unwrap(addresstype)
|
||||
.remove(AddressIndexTxIndex::from((addressindex, txindex)));
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user