mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-01 18:53:38 -07:00
global: snapshot
This commit is contained in:
Generated
+4
@@ -576,10 +576,12 @@ dependencies = [
|
|||||||
name = "brk_binder"
|
name = "brk_binder"
|
||||||
version = "0.1.0-alpha.1"
|
version = "0.1.0-alpha.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"brk_grouper",
|
||||||
"brk_query",
|
"brk_query",
|
||||||
"brk_types",
|
"brk_types",
|
||||||
"oas3",
|
"oas3",
|
||||||
"schemars",
|
"schemars",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"vecdb",
|
"vecdb",
|
||||||
]
|
]
|
||||||
@@ -628,6 +630,7 @@ dependencies = [
|
|||||||
name = "brk_client"
|
name = "brk_client"
|
||||||
version = "0.1.0-alpha.1"
|
version = "0.1.0-alpha.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"brk_grouper",
|
||||||
"brk_types",
|
"brk_types",
|
||||||
"minreq",
|
"minreq",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -698,6 +701,7 @@ dependencies = [
|
|||||||
"brk_traversable",
|
"brk_traversable",
|
||||||
"brk_types",
|
"brk_types",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"serde",
|
||||||
"vecdb",
|
"vecdb",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ repository.workspace = true
|
|||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
brk_grouper = { workspace = true }
|
||||||
brk_query = { workspace = true }
|
brk_query = { workspace = true }
|
||||||
brk_types = { workspace = true }
|
brk_types = { workspace = true }
|
||||||
oas3 = "0.20"
|
oas3 = "0.20"
|
||||||
schemars = { workspace = true }
|
schemars = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
vecdb = { workspace = true }
|
vecdb = { workspace = true }
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
use std::{collections::HashSet, fmt::Write as FmtWrite, fs, io, path::Path};
|
use std::{collections::HashSet, fmt::Write as FmtWrite, fs, io, path::Path};
|
||||||
|
|
||||||
|
use brk_grouper::{
|
||||||
|
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, EPOCH_NAMES, GE_AMOUNT_NAMES, LT_AMOUNT_NAMES,
|
||||||
|
MAX_AGE_NAMES, MIN_AGE_NAMES, SPENDABLE_TYPE_NAMES, TERM_NAMES, YEAR_NAMES,
|
||||||
|
};
|
||||||
use brk_types::{pools, Index, TreeNode};
|
use brk_types::{pools, Index, TreeNode};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
@@ -60,6 +64,36 @@ fn generate_constants(output: &mut String) {
|
|||||||
writeln!(output, " {}: \"{}\",", pool.slug(), pool.name).unwrap();
|
writeln!(output, " {}: \"{}\",", pool.slug(), pool.name).unwrap();
|
||||||
}
|
}
|
||||||
writeln!(output, "}});\n").unwrap();
|
writeln!(output, "}});\n").unwrap();
|
||||||
|
|
||||||
|
// Cohort names - serialize from brk_grouper using serde_json
|
||||||
|
generate_cohort_names(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_cohort_names(output: &mut String) {
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
fn export_const<T: Serialize>(output: &mut String, name: &str, value: &T) {
|
||||||
|
let json = serde_json::to_string_pretty(value).unwrap();
|
||||||
|
writeln!(
|
||||||
|
output,
|
||||||
|
"export const {} = /** @type {{const}} */ ({});\n",
|
||||||
|
name, json
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(output, "// Cohort names\n").unwrap();
|
||||||
|
|
||||||
|
export_const(output, "TERM_NAMES", &TERM_NAMES);
|
||||||
|
export_const(output, "EPOCH_NAMES", &EPOCH_NAMES);
|
||||||
|
export_const(output, "YEAR_NAMES", &YEAR_NAMES);
|
||||||
|
export_const(output, "SPENDABLE_TYPE_NAMES", &SPENDABLE_TYPE_NAMES);
|
||||||
|
export_const(output, "AGE_RANGE_NAMES", &AGE_RANGE_NAMES);
|
||||||
|
export_const(output, "MAX_AGE_NAMES", &MAX_AGE_NAMES);
|
||||||
|
export_const(output, "MIN_AGE_NAMES", &MIN_AGE_NAMES);
|
||||||
|
export_const(output, "AMOUNT_RANGE_NAMES", &AMOUNT_RANGE_NAMES);
|
||||||
|
export_const(output, "GE_AMOUNT_NAMES", &GE_AMOUNT_NAMES);
|
||||||
|
export_const(output, "LT_AMOUNT_NAMES", <_AMOUNT_NAMES);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
use std::{collections::HashSet, fmt::Write as FmtWrite, fs, io, path::Path};
|
use std::{collections::HashSet, fmt::Write as FmtWrite, fs, io, path::Path};
|
||||||
|
|
||||||
|
use brk_grouper::{
|
||||||
|
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, EPOCH_NAMES, GE_AMOUNT_NAMES, LT_AMOUNT_NAMES,
|
||||||
|
MAX_AGE_NAMES, MIN_AGE_NAMES, SPENDABLE_TYPE_NAMES, TERM_NAMES, YEAR_NAMES,
|
||||||
|
};
|
||||||
use brk_types::{pools, Index, TreeNode};
|
use brk_types::{pools, Index, TreeNode};
|
||||||
|
use serde::Serialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -69,6 +74,29 @@ fn generate_constants(output: &mut String) {
|
|||||||
writeln!(output, " \"{}\": \"{}\",", pool.slug(), pool.name).unwrap();
|
writeln!(output, " \"{}\": \"{}\",", pool.slug(), pool.name).unwrap();
|
||||||
}
|
}
|
||||||
writeln!(output, "}}\n").unwrap();
|
writeln!(output, "}}\n").unwrap();
|
||||||
|
|
||||||
|
// Cohort names
|
||||||
|
generate_cohort_names(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_cohort_names(output: &mut String) {
|
||||||
|
fn export_const<T: Serialize>(output: &mut String, name: &str, value: &T) {
|
||||||
|
let json = serde_json::to_string_pretty(value).unwrap();
|
||||||
|
writeln!(output, "{}: Final = {}\n", name, json).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(output, "# Cohort names\n").unwrap();
|
||||||
|
|
||||||
|
export_const(output, "TERM_NAMES", &TERM_NAMES);
|
||||||
|
export_const(output, "EPOCH_NAMES", &EPOCH_NAMES);
|
||||||
|
export_const(output, "YEAR_NAMES", &YEAR_NAMES);
|
||||||
|
export_const(output, "SPENDABLE_TYPE_NAMES", &SPENDABLE_TYPE_NAMES);
|
||||||
|
export_const(output, "AGE_RANGE_NAMES", &AGE_RANGE_NAMES);
|
||||||
|
export_const(output, "MAX_AGE_NAMES", &MAX_AGE_NAMES);
|
||||||
|
export_const(output, "MIN_AGE_NAMES", &MIN_AGE_NAMES);
|
||||||
|
export_const(output, "AMOUNT_RANGE_NAMES", &AMOUNT_RANGE_NAMES);
|
||||||
|
export_const(output, "GE_AMOUNT_NAMES", &GE_AMOUNT_NAMES);
|
||||||
|
export_const(output, "LT_AMOUNT_NAMES", <_AMOUNT_NAMES);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ pub fn generate_rust_client(
|
|||||||
writeln!(output, "// Auto-generated BRK Rust client").unwrap();
|
writeln!(output, "// Auto-generated BRK Rust client").unwrap();
|
||||||
writeln!(output, "// Do not edit manually\n").unwrap();
|
writeln!(output, "// Do not edit manually\n").unwrap();
|
||||||
writeln!(output, "#![allow(non_camel_case_types)]").unwrap();
|
writeln!(output, "#![allow(non_camel_case_types)]").unwrap();
|
||||||
writeln!(output, "#![allow(dead_code)]\n").unwrap();
|
writeln!(output, "#![allow(dead_code)]").unwrap();
|
||||||
|
writeln!(output, "#![allow(unused_variables)]").unwrap();
|
||||||
|
writeln!(output, "#![allow(clippy::useless_format)]\n").unwrap();
|
||||||
|
|
||||||
generate_imports(&mut output);
|
generate_imports(&mut output);
|
||||||
generate_base_client(&mut output);
|
generate_base_client(&mut output);
|
||||||
@@ -41,7 +43,8 @@ fn generate_imports(output: &mut String) {
|
|||||||
output,
|
output,
|
||||||
r#"use std::sync::Arc;
|
r#"use std::sync::Arc;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use brk_types::*;
|
pub use brk_grouper::*;
|
||||||
|
pub use brk_types::*;
|
||||||
|
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ pub fn to_pascal_case(s: &str) -> String {
|
|||||||
|
|
||||||
/// Convert a string to snake_case, handling Rust keywords.
|
/// Convert a string to snake_case, handling Rust keywords.
|
||||||
pub fn to_snake_case(s: &str) -> String {
|
pub fn to_snake_case(s: &str) -> String {
|
||||||
let sanitized = s.replace('-', "_");
|
// Convert to lowercase and replace dashes with underscores
|
||||||
|
let sanitized = s.to_lowercase().replace('-', "_");
|
||||||
|
|
||||||
// Prefix with _ if starts with digit
|
// Prefix with _ if starts with digit
|
||||||
let sanitized = if sanitized.chars().next().is_some_and(|c| c.is_ascii_digit()) {
|
let sanitized = if sanitized.chars().next().is_some_and(|c| c.is_ascii_digit()) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ repository.workspace = true
|
|||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
brk_grouper = { workspace = true }
|
||||||
brk_types = { workspace = true }
|
brk_types = { workspace = true }
|
||||||
minreq = { workspace = true }
|
minreq = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
|||||||
+502
-492
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
use brk_types::{Bitcoin, Close, Dollars, High, Sats, StoredF32, StoredF64, StoredU32};
|
use brk_types::{Bitcoin, Close, Dollars, Sats, StoredF32, StoredF64, StoredU32};
|
||||||
use vecdb::{BinaryTransform, UnaryTransform};
|
use vecdb::{BinaryTransform, UnaryTransform};
|
||||||
|
|
||||||
/// (Dollars, Dollars) -> Dollars addition
|
/// (Dollars, Dollars) -> Dollars addition
|
||||||
@@ -320,18 +320,3 @@ impl BinaryTransform<Close<Dollars>, Dollars, StoredF32> for PercentageDiffClose
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// (High<Dollars>, Dollars) -> StoredF32 percentage difference ((a/b - 1) × 100)
|
|
||||||
/// Used for drawdown calculation from high prices
|
|
||||||
pub struct PercentageDiffHighDollars;
|
|
||||||
|
|
||||||
impl BinaryTransform<High<Dollars>, Dollars, StoredF32> for PercentageDiffHighDollars {
|
|
||||||
#[inline(always)]
|
|
||||||
fn apply(high: High<Dollars>, base: Dollars) -> StoredF32 {
|
|
||||||
if base == Dollars::ZERO {
|
|
||||||
StoredF32::default()
|
|
||||||
} else {
|
|
||||||
StoredF32::from((**high / *base - 1.0) * 100.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -50,9 +50,11 @@ impl AddressCohortVecs {
|
|||||||
///
|
///
|
||||||
/// `all_supply` is the supply metrics from the "all" cohort, used as global
|
/// `all_supply` is the supply metrics from the "all" cohort, used as global
|
||||||
/// sources for `*_rel_to_market_cap` ratios. Pass `None` if not available.
|
/// sources for `*_rel_to_market_cap` ratios. Pass `None` if not available.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn forced_import(
|
pub fn forced_import(
|
||||||
db: &Database,
|
db: &Database,
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
|
name: &str,
|
||||||
version: Version,
|
version: Version,
|
||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
price: Option<&price::Vecs>,
|
price: Option<&price::Vecs>,
|
||||||
@@ -60,11 +62,12 @@ impl AddressCohortVecs {
|
|||||||
all_supply: Option<&SupplyMetrics>,
|
all_supply: Option<&SupplyMetrics>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let compute_dollars = price.is_some();
|
let compute_dollars = price.is_some();
|
||||||
let full_name = filter.to_full_name(CohortContext::Address);
|
let full_name = CohortContext::Address.full_name(&filter, name);
|
||||||
|
|
||||||
let cfg = ImportConfig {
|
let cfg = ImportConfig {
|
||||||
db,
|
db,
|
||||||
filter,
|
filter,
|
||||||
|
full_name: &full_name,
|
||||||
context: CohortContext::Address,
|
context: CohortContext::Address,
|
||||||
version,
|
version,
|
||||||
indexes,
|
indexes,
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ use std::path::Path;
|
|||||||
|
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_grouper::{
|
use brk_grouper::{
|
||||||
AddressGroups, AmountFilter, ByAmountRange, ByGreatEqualAmount, ByLowerThanAmount, Filter,
|
AddressGroups, ByAmountRange, ByGreatEqualAmount, ByLowerThanAmount, Filter, Filtered,
|
||||||
Filtered,
|
|
||||||
};
|
};
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{Bitcoin, DateIndex, Dollars, Height, Sats, Version};
|
use brk_types::{Bitcoin, DateIndex, Dollars, Height, Version};
|
||||||
use derive_deref::{Deref, DerefMut};
|
use derive_deref::{Deref, DerefMut};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use vecdb::{AnyStoredVec, Database, Exit, IterableVec};
|
use vecdb::{AnyStoredVec, Database, Exit, IterableVec};
|
||||||
@@ -37,90 +36,19 @@ impl AddressCohorts {
|
|||||||
let v = version + VERSION + Version::ZERO;
|
let v = version + VERSION + Version::ZERO;
|
||||||
|
|
||||||
// Helper to create a cohort - only amount_range cohorts have state
|
// Helper to create a cohort - only amount_range cohorts have state
|
||||||
let create = |filter: Filter, has_state: bool| -> Result<AddressCohortVecs> {
|
let create =
|
||||||
let states_path = if has_state { Some(states_path) } else { None };
|
|filter: Filter, name: &'static str, has_state: bool| -> Result<AddressCohortVecs> {
|
||||||
AddressCohortVecs::forced_import(db, filter, v, indexes, price, states_path, all_supply)
|
let sp = if has_state { Some(states_path) } else { None };
|
||||||
};
|
AddressCohortVecs::forced_import(db, filter, name, v, indexes, price, sp, all_supply)
|
||||||
|
};
|
||||||
|
|
||||||
let full = |f: Filter| create(f, true);
|
let full = |f: Filter, name: &'static str| create(f, name, true);
|
||||||
let none = |f: Filter| create(f, false);
|
let none = |f: Filter, name: &'static str| create(f, name, false);
|
||||||
|
|
||||||
Ok(Self(AddressGroups {
|
Ok(Self(AddressGroups {
|
||||||
amount_range: ByAmountRange {
|
amount_range: ByAmountRange::try_new(&full)?,
|
||||||
_0sats: full(Filter::Amount(AmountFilter::LowerThan(Sats::_1)))?,
|
lt_amount: ByLowerThanAmount::try_new(&none)?,
|
||||||
_1sat_to_10sats: full(Filter::Amount(AmountFilter::Range(Sats::_1..Sats::_10)))?,
|
ge_amount: ByGreatEqualAmount::try_new(&none)?,
|
||||||
_10sats_to_100sats: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_10..Sats::_100,
|
|
||||||
)))?,
|
|
||||||
_100sats_to_1k_sats: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_100..Sats::_1K,
|
|
||||||
)))?,
|
|
||||||
_1k_sats_to_10k_sats: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_1K..Sats::_10K,
|
|
||||||
)))?,
|
|
||||||
_10k_sats_to_100k_sats: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_10K..Sats::_100K,
|
|
||||||
)))?,
|
|
||||||
_100k_sats_to_1m_sats: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_100K..Sats::_1M,
|
|
||||||
)))?,
|
|
||||||
_1m_sats_to_10m_sats: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_1M..Sats::_10M,
|
|
||||||
)))?,
|
|
||||||
_10m_sats_to_1btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_10M..Sats::_1BTC,
|
|
||||||
)))?,
|
|
||||||
_1btc_to_10btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_1BTC..Sats::_10BTC,
|
|
||||||
)))?,
|
|
||||||
_10btc_to_100btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_10BTC..Sats::_100BTC,
|
|
||||||
)))?,
|
|
||||||
_100btc_to_1k_btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_100BTC..Sats::_1K_BTC,
|
|
||||||
)))?,
|
|
||||||
_1k_btc_to_10k_btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_1K_BTC..Sats::_10K_BTC,
|
|
||||||
)))?,
|
|
||||||
_10k_btc_to_100k_btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_10K_BTC..Sats::_100K_BTC,
|
|
||||||
)))?,
|
|
||||||
_100k_btc_or_more: full(Filter::Amount(AmountFilter::GreaterOrEqual(
|
|
||||||
Sats::_100K_BTC,
|
|
||||||
)))?,
|
|
||||||
},
|
|
||||||
|
|
||||||
lt_amount: ByLowerThanAmount {
|
|
||||||
_10sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10)))?,
|
|
||||||
_100sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100)))?,
|
|
||||||
_1k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K)))?,
|
|
||||||
_10k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K)))?,
|
|
||||||
_100k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K)))?,
|
|
||||||
_1m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1M)))?,
|
|
||||||
_10m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10M)))?,
|
|
||||||
_1btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1BTC)))?,
|
|
||||||
_10btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10BTC)))?,
|
|
||||||
_100btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100BTC)))?,
|
|
||||||
_1k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K_BTC)))?,
|
|
||||||
_10k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K_BTC)))?,
|
|
||||||
_100k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K_BTC)))?,
|
|
||||||
},
|
|
||||||
|
|
||||||
ge_amount: ByGreatEqualAmount {
|
|
||||||
_1sat: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1)))?,
|
|
||||||
_10sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10)))?,
|
|
||||||
_100sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100)))?,
|
|
||||||
_1k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K)))?,
|
|
||||||
_10k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K)))?,
|
|
||||||
_100k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100K)))?,
|
|
||||||
_1m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1M)))?,
|
|
||||||
_10m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10M)))?,
|
|
||||||
_1btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1BTC)))?,
|
|
||||||
_10btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10BTC)))?,
|
|
||||||
_100btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100BTC)))?,
|
|
||||||
_1k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K_BTC)))?,
|
|
||||||
_10k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K_BTC)))?,
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ impl UTXOCohortVecs {
|
|||||||
pub fn forced_import(
|
pub fn forced_import(
|
||||||
db: &Database,
|
db: &Database,
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
|
name: &str,
|
||||||
version: Version,
|
version: Version,
|
||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
price: Option<&price::Vecs>,
|
price: Option<&price::Vecs>,
|
||||||
@@ -46,11 +47,12 @@ impl UTXOCohortVecs {
|
|||||||
all_supply: Option<&SupplyMetrics>,
|
all_supply: Option<&SupplyMetrics>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let compute_dollars = price.is_some();
|
let compute_dollars = price.is_some();
|
||||||
let full_name = filter.to_full_name(CohortContext::Utxo);
|
let full_name = CohortContext::Utxo.full_name(&filter, name);
|
||||||
|
|
||||||
let cfg = ImportConfig {
|
let cfg = ImportConfig {
|
||||||
db,
|
db,
|
||||||
filter,
|
filter,
|
||||||
|
full_name: &full_name,
|
||||||
context: CohortContext::Utxo,
|
context: CohortContext::Utxo,
|
||||||
version,
|
version,
|
||||||
indexes,
|
indexes,
|
||||||
|
|||||||
@@ -6,16 +6,11 @@ use std::path::Path;
|
|||||||
|
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_grouper::{
|
use brk_grouper::{
|
||||||
AmountFilter, ByAgeRange, ByAmountRange, ByEpoch, ByGreatEqualAmount, ByLowerThanAmount,
|
ByAgeRange, ByAmountRange, ByEpoch, ByGreatEqualAmount, ByLowerThanAmount, ByMaxAge, ByMinAge,
|
||||||
ByMaxAge, ByMinAge, BySpendableType, ByTerm, ByYear, DAYS_1D, DAYS_1M, DAYS_1W, DAYS_1Y,
|
BySpendableType, ByTerm, ByYear, Filter, Filtered, StateLevel, UTXOGroups,
|
||||||
DAYS_2M, DAYS_2Y, DAYS_3M, DAYS_3Y, DAYS_4M, DAYS_4Y, DAYS_5M, DAYS_5Y, DAYS_6M, DAYS_6Y,
|
|
||||||
DAYS_7Y, DAYS_8Y, DAYS_10Y, DAYS_12Y, DAYS_15Y, Filter, Filtered, StateLevel, Term, TimeFilter,
|
|
||||||
UTXOGroups,
|
|
||||||
};
|
};
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{
|
use brk_types::{Bitcoin, DateIndex, Dollars, Height, Sats, Version};
|
||||||
Bitcoin, DateIndex, Dollars, HalvingEpoch, Height, OutputType, Sats, Version, Year,
|
|
||||||
};
|
|
||||||
use derive_deref::{Deref, DerefMut};
|
use derive_deref::{Deref, DerefMut};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use vecdb::{AnyStoredVec, Database, Exit, IterableVec};
|
use vecdb::{AnyStoredVec, Database, Exit, IterableVec};
|
||||||
@@ -50,6 +45,7 @@ impl UTXOCohorts {
|
|||||||
let all = UTXOCohortVecs::forced_import(
|
let all = UTXOCohortVecs::forced_import(
|
||||||
db,
|
db,
|
||||||
Filter::All,
|
Filter::All,
|
||||||
|
"",
|
||||||
version + VERSION + Version::ONE,
|
version + VERSION + Version::ONE,
|
||||||
indexes,
|
indexes,
|
||||||
price,
|
price,
|
||||||
@@ -62,227 +58,34 @@ impl UTXOCohorts {
|
|||||||
let all_supply = Some(&all.metrics.supply);
|
let all_supply = Some(&all.metrics.supply);
|
||||||
|
|
||||||
// Create all cohorts first (while borrowing all_supply), then assemble struct
|
// Create all cohorts first (while borrowing all_supply), then assemble struct
|
||||||
let term = ByTerm {
|
let price_only = |f: Filter, name: &'static str| {
|
||||||
short: UTXOCohortVecs::forced_import(
|
|
||||||
db,
|
|
||||||
Filter::Term(Term::Sth),
|
|
||||||
v,
|
|
||||||
indexes,
|
|
||||||
price,
|
|
||||||
states_path,
|
|
||||||
StateLevel::PriceOnly,
|
|
||||||
all_supply,
|
|
||||||
)?,
|
|
||||||
long: UTXOCohortVecs::forced_import(
|
|
||||||
db,
|
|
||||||
Filter::Term(Term::Lth),
|
|
||||||
v,
|
|
||||||
indexes,
|
|
||||||
price,
|
|
||||||
states_path,
|
|
||||||
StateLevel::PriceOnly,
|
|
||||||
all_supply,
|
|
||||||
)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let full = |f: Filter| {
|
|
||||||
UTXOCohortVecs::forced_import(
|
UTXOCohortVecs::forced_import(
|
||||||
db,
|
db, f, name, v, indexes, price, states_path, StateLevel::PriceOnly, all_supply,
|
||||||
f,
|
|
||||||
v,
|
|
||||||
indexes,
|
|
||||||
price,
|
|
||||||
states_path,
|
|
||||||
StateLevel::Full,
|
|
||||||
all_supply,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let none = |f: Filter| {
|
|
||||||
UTXOCohortVecs::forced_import(
|
|
||||||
db,
|
|
||||||
f,
|
|
||||||
v,
|
|
||||||
indexes,
|
|
||||||
price,
|
|
||||||
states_path,
|
|
||||||
StateLevel::None,
|
|
||||||
all_supply,
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let epoch = ByEpoch {
|
let term = ByTerm::try_new(&price_only)?;
|
||||||
_0: full(Filter::Epoch(HalvingEpoch::new(0)))?,
|
|
||||||
_1: full(Filter::Epoch(HalvingEpoch::new(1)))?,
|
let full = |f: Filter, name: &'static str| {
|
||||||
_2: full(Filter::Epoch(HalvingEpoch::new(2)))?,
|
UTXOCohortVecs::forced_import(
|
||||||
_3: full(Filter::Epoch(HalvingEpoch::new(3)))?,
|
db, f, name, v, indexes, price, states_path, StateLevel::Full, all_supply,
|
||||||
_4: full(Filter::Epoch(HalvingEpoch::new(4)))?,
|
)
|
||||||
|
};
|
||||||
|
let none = |f: Filter, name: &'static str| {
|
||||||
|
UTXOCohortVecs::forced_import(
|
||||||
|
db, f, name, v, indexes, price, states_path, StateLevel::None, all_supply,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let year = ByYear {
|
let epoch = ByEpoch::try_new(&full)?;
|
||||||
_2009: full(Filter::Year(Year::new(2009)))?,
|
let year = ByYear::try_new(&full)?;
|
||||||
_2010: full(Filter::Year(Year::new(2010)))?,
|
let type_ = BySpendableType::try_new(&full)?;
|
||||||
_2011: full(Filter::Year(Year::new(2011)))?,
|
let max_age = ByMaxAge::try_new(&none)?;
|
||||||
_2012: full(Filter::Year(Year::new(2012)))?,
|
let min_age = ByMinAge::try_new(&none)?;
|
||||||
_2013: full(Filter::Year(Year::new(2013)))?,
|
let age_range = ByAgeRange::try_new(&full)?;
|
||||||
_2014: full(Filter::Year(Year::new(2014)))?,
|
let amount_range = ByAmountRange::try_new(&full)?;
|
||||||
_2015: full(Filter::Year(Year::new(2015)))?,
|
let lt_amount = ByLowerThanAmount::try_new(&none)?;
|
||||||
_2016: full(Filter::Year(Year::new(2016)))?,
|
let ge_amount = ByGreatEqualAmount::try_new(&none)?;
|
||||||
_2017: full(Filter::Year(Year::new(2017)))?,
|
|
||||||
_2018: full(Filter::Year(Year::new(2018)))?,
|
|
||||||
_2019: full(Filter::Year(Year::new(2019)))?,
|
|
||||||
_2020: full(Filter::Year(Year::new(2020)))?,
|
|
||||||
_2021: full(Filter::Year(Year::new(2021)))?,
|
|
||||||
_2022: full(Filter::Year(Year::new(2022)))?,
|
|
||||||
_2023: full(Filter::Year(Year::new(2023)))?,
|
|
||||||
_2024: full(Filter::Year(Year::new(2024)))?,
|
|
||||||
_2025: full(Filter::Year(Year::new(2025)))?,
|
|
||||||
_2026: full(Filter::Year(Year::new(2026)))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_ = BySpendableType {
|
|
||||||
p2pk65: full(Filter::Type(OutputType::P2PK65))?,
|
|
||||||
p2pk33: full(Filter::Type(OutputType::P2PK33))?,
|
|
||||||
p2pkh: full(Filter::Type(OutputType::P2PKH))?,
|
|
||||||
p2sh: full(Filter::Type(OutputType::P2SH))?,
|
|
||||||
p2wpkh: full(Filter::Type(OutputType::P2WPKH))?,
|
|
||||||
p2wsh: full(Filter::Type(OutputType::P2WSH))?,
|
|
||||||
p2tr: full(Filter::Type(OutputType::P2TR))?,
|
|
||||||
p2a: full(Filter::Type(OutputType::P2A))?,
|
|
||||||
p2ms: full(Filter::Type(OutputType::P2MS))?,
|
|
||||||
empty: full(Filter::Type(OutputType::Empty))?,
|
|
||||||
unknown: full(Filter::Type(OutputType::Unknown))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let max_age = ByMaxAge {
|
|
||||||
_1w: none(Filter::Time(TimeFilter::LowerThan(DAYS_1W)))?,
|
|
||||||
_1m: none(Filter::Time(TimeFilter::LowerThan(DAYS_1M)))?,
|
|
||||||
_2m: none(Filter::Time(TimeFilter::LowerThan(DAYS_2M)))?,
|
|
||||||
_3m: none(Filter::Time(TimeFilter::LowerThan(DAYS_3M)))?,
|
|
||||||
_4m: none(Filter::Time(TimeFilter::LowerThan(DAYS_4M)))?,
|
|
||||||
_5m: none(Filter::Time(TimeFilter::LowerThan(DAYS_5M)))?,
|
|
||||||
_6m: none(Filter::Time(TimeFilter::LowerThan(DAYS_6M)))?,
|
|
||||||
_1y: none(Filter::Time(TimeFilter::LowerThan(DAYS_1Y)))?,
|
|
||||||
_2y: none(Filter::Time(TimeFilter::LowerThan(DAYS_2Y)))?,
|
|
||||||
_3y: none(Filter::Time(TimeFilter::LowerThan(DAYS_3Y)))?,
|
|
||||||
_4y: none(Filter::Time(TimeFilter::LowerThan(DAYS_4Y)))?,
|
|
||||||
_5y: none(Filter::Time(TimeFilter::LowerThan(DAYS_5Y)))?,
|
|
||||||
_6y: none(Filter::Time(TimeFilter::LowerThan(DAYS_6Y)))?,
|
|
||||||
_7y: none(Filter::Time(TimeFilter::LowerThan(DAYS_7Y)))?,
|
|
||||||
_8y: none(Filter::Time(TimeFilter::LowerThan(DAYS_8Y)))?,
|
|
||||||
_10y: none(Filter::Time(TimeFilter::LowerThan(DAYS_10Y)))?,
|
|
||||||
_12y: none(Filter::Time(TimeFilter::LowerThan(DAYS_12Y)))?,
|
|
||||||
_15y: none(Filter::Time(TimeFilter::LowerThan(DAYS_15Y)))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let min_age = ByMinAge {
|
|
||||||
_1d: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1D)))?,
|
|
||||||
_1w: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1W)))?,
|
|
||||||
_1m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1M)))?,
|
|
||||||
_2m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2M)))?,
|
|
||||||
_3m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3M)))?,
|
|
||||||
_4m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4M)))?,
|
|
||||||
_5m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5M)))?,
|
|
||||||
_6m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6M)))?,
|
|
||||||
_1y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1Y)))?,
|
|
||||||
_2y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2Y)))?,
|
|
||||||
_3y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3Y)))?,
|
|
||||||
_4y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4Y)))?,
|
|
||||||
_5y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5Y)))?,
|
|
||||||
_6y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6Y)))?,
|
|
||||||
_7y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_7Y)))?,
|
|
||||||
_8y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_8Y)))?,
|
|
||||||
_10y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_10Y)))?,
|
|
||||||
_12y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_12Y)))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let age_range = ByAgeRange {
|
|
||||||
up_to_1d: full(Filter::Time(TimeFilter::Range(0..DAYS_1D)))?,
|
|
||||||
_1d_to_1w: full(Filter::Time(TimeFilter::Range(DAYS_1D..DAYS_1W)))?,
|
|
||||||
_1w_to_1m: full(Filter::Time(TimeFilter::Range(DAYS_1W..DAYS_1M)))?,
|
|
||||||
_1m_to_2m: full(Filter::Time(TimeFilter::Range(DAYS_1M..DAYS_2M)))?,
|
|
||||||
_2m_to_3m: full(Filter::Time(TimeFilter::Range(DAYS_2M..DAYS_3M)))?,
|
|
||||||
_3m_to_4m: full(Filter::Time(TimeFilter::Range(DAYS_3M..DAYS_4M)))?,
|
|
||||||
_4m_to_5m: full(Filter::Time(TimeFilter::Range(DAYS_4M..DAYS_5M)))?,
|
|
||||||
_5m_to_6m: full(Filter::Time(TimeFilter::Range(DAYS_5M..DAYS_6M)))?,
|
|
||||||
_6m_to_1y: full(Filter::Time(TimeFilter::Range(DAYS_6M..DAYS_1Y)))?,
|
|
||||||
_1y_to_2y: full(Filter::Time(TimeFilter::Range(DAYS_1Y..DAYS_2Y)))?,
|
|
||||||
_2y_to_3y: full(Filter::Time(TimeFilter::Range(DAYS_2Y..DAYS_3Y)))?,
|
|
||||||
_3y_to_4y: full(Filter::Time(TimeFilter::Range(DAYS_3Y..DAYS_4Y)))?,
|
|
||||||
_4y_to_5y: full(Filter::Time(TimeFilter::Range(DAYS_4Y..DAYS_5Y)))?,
|
|
||||||
_5y_to_6y: full(Filter::Time(TimeFilter::Range(DAYS_5Y..DAYS_6Y)))?,
|
|
||||||
_6y_to_7y: full(Filter::Time(TimeFilter::Range(DAYS_6Y..DAYS_7Y)))?,
|
|
||||||
_7y_to_8y: full(Filter::Time(TimeFilter::Range(DAYS_7Y..DAYS_8Y)))?,
|
|
||||||
_8y_to_10y: full(Filter::Time(TimeFilter::Range(DAYS_8Y..DAYS_10Y)))?,
|
|
||||||
_10y_to_12y: full(Filter::Time(TimeFilter::Range(DAYS_10Y..DAYS_12Y)))?,
|
|
||||||
_12y_to_15y: full(Filter::Time(TimeFilter::Range(DAYS_12Y..DAYS_15Y)))?,
|
|
||||||
from_15y: full(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_15Y)))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let amount_range = ByAmountRange {
|
|
||||||
_0sats: full(Filter::Amount(AmountFilter::LowerThan(Sats::_1)))?,
|
|
||||||
_1sat_to_10sats: full(Filter::Amount(AmountFilter::Range(Sats::_1..Sats::_10)))?,
|
|
||||||
_10sats_to_100sats: full(Filter::Amount(AmountFilter::Range(Sats::_10..Sats::_100)))?,
|
|
||||||
_100sats_to_1k_sats: full(Filter::Amount(AmountFilter::Range(Sats::_100..Sats::_1K)))?,
|
|
||||||
_1k_sats_to_10k_sats: full(Filter::Amount(AmountFilter::Range(Sats::_1K..Sats::_10K)))?,
|
|
||||||
_10k_sats_to_100k_sats: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_10K..Sats::_100K,
|
|
||||||
)))?,
|
|
||||||
_100k_sats_to_1m_sats: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_100K..Sats::_1M,
|
|
||||||
)))?,
|
|
||||||
_1m_sats_to_10m_sats: full(Filter::Amount(AmountFilter::Range(Sats::_1M..Sats::_10M)))?,
|
|
||||||
_10m_sats_to_1btc: full(Filter::Amount(AmountFilter::Range(Sats::_10M..Sats::_1BTC)))?,
|
|
||||||
_1btc_to_10btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_1BTC..Sats::_10BTC,
|
|
||||||
)))?,
|
|
||||||
_10btc_to_100btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_10BTC..Sats::_100BTC,
|
|
||||||
)))?,
|
|
||||||
_100btc_to_1k_btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_100BTC..Sats::_1K_BTC,
|
|
||||||
)))?,
|
|
||||||
_1k_btc_to_10k_btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_1K_BTC..Sats::_10K_BTC,
|
|
||||||
)))?,
|
|
||||||
_10k_btc_to_100k_btc: full(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_10K_BTC..Sats::_100K_BTC,
|
|
||||||
)))?,
|
|
||||||
_100k_btc_or_more: full(Filter::Amount(AmountFilter::GreaterOrEqual(
|
|
||||||
Sats::_100K_BTC,
|
|
||||||
)))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let lt_amount = ByLowerThanAmount {
|
|
||||||
_10sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10)))?,
|
|
||||||
_100sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100)))?,
|
|
||||||
_1k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K)))?,
|
|
||||||
_10k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K)))?,
|
|
||||||
_100k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K)))?,
|
|
||||||
_1m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1M)))?,
|
|
||||||
_10m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10M)))?,
|
|
||||||
_1btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1BTC)))?,
|
|
||||||
_10btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10BTC)))?,
|
|
||||||
_100btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100BTC)))?,
|
|
||||||
_1k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K_BTC)))?,
|
|
||||||
_10k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K_BTC)))?,
|
|
||||||
_100k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K_BTC)))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let ge_amount = ByGreatEqualAmount {
|
|
||||||
_1sat: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1)))?,
|
|
||||||
_10sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10)))?,
|
|
||||||
_100sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100)))?,
|
|
||||||
_1k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K)))?,
|
|
||||||
_10k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K)))?,
|
|
||||||
_100k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100K)))?,
|
|
||||||
_1m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1M)))?,
|
|
||||||
_10m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10M)))?,
|
|
||||||
_1btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1BTC)))?,
|
|
||||||
_10btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10BTC)))?,
|
|
||||||
_100btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100BTC)))?,
|
|
||||||
_1k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K_BTC)))?,
|
|
||||||
_10k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K_BTC)))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self(UTXOGroups {
|
Ok(Self(UTXOGroups {
|
||||||
all,
|
all,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use crate::{indexes, price};
|
|||||||
pub struct ImportConfig<'a> {
|
pub struct ImportConfig<'a> {
|
||||||
pub db: &'a Database,
|
pub db: &'a Database,
|
||||||
pub filter: Filter,
|
pub filter: Filter,
|
||||||
|
pub full_name: &'a str,
|
||||||
pub context: CohortContext,
|
pub context: CohortContext,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub indexes: &'a indexes::Vecs,
|
pub indexes: &'a indexes::Vecs,
|
||||||
@@ -37,11 +38,10 @@ impl<'a> ImportConfig<'a> {
|
|||||||
|
|
||||||
/// Get full metric name with filter prefix.
|
/// Get full metric name with filter prefix.
|
||||||
pub fn name(&self, suffix: &str) -> String {
|
pub fn name(&self, suffix: &str) -> String {
|
||||||
let prefix = self.filter.to_full_name(self.context);
|
if self.full_name.is_empty() {
|
||||||
if prefix.is_empty() {
|
|
||||||
suffix.to_string()
|
suffix.to_string()
|
||||||
} else {
|
} else {
|
||||||
format!("{prefix}_{suffix}")
|
format!("{}_{suffix}", self.full_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,3 +14,4 @@ brk_types = { workspace = true }
|
|||||||
brk_traversable = { workspace = true }
|
brk_traversable = { workspace = true }
|
||||||
vecdb = { workspace = true }
|
vecdb = { workspace = true }
|
||||||
rayon = { workspace = true }
|
rayon = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
|||||||
@@ -38,8 +38,9 @@ let filter = Filter::Time(TimeFilter::GreaterOrEqual(155));
|
|||||||
filter.contains_time(200); // true
|
filter.contains_time(200); // true
|
||||||
filter.contains_amount(sats);
|
filter.contains_amount(sats);
|
||||||
|
|
||||||
// Generate metric names
|
// Generate metric names (via CohortContext)
|
||||||
filter.to_full_name(CohortContext::Utxo); // "utxos_min_age_155d"
|
let ctx = CohortContext::Utxo;
|
||||||
|
ctx.full_name(&filter, "min_age_155d"); // "utxos_min_age_155d"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Built On
|
## Built On
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub struct AddressGroups<T> {
|
|||||||
impl<T> AddressGroups<T> {
|
impl<T> AddressGroups<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
ge_amount: ByGreatEqualAmount::new(&mut create),
|
ge_amount: ByGreatEqualAmount::new(&mut create),
|
||||||
@@ -25,6 +25,17 @@ impl<T> AddressGroups<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(create: &F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: Fn(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
Ok(Self {
|
||||||
|
ge_amount: ByGreatEqualAmount::try_new(create)?,
|
||||||
|
amount_range: ByAmountRange::try_new(create)?,
|
||||||
|
lt_amount: ByLowerThanAmount::try_new(create)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
self.ge_amount
|
self.ge_amount
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -33,40 +33,4 @@ impl AmountFilter {
|
|||||||
AmountFilter::Range(_) => false,
|
AmountFilter::Range(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_name_suffix(&self) -> String {
|
|
||||||
match self {
|
|
||||||
AmountFilter::LowerThan(s) if *s == Sats::_1 => "with_0sats".to_string(),
|
|
||||||
AmountFilter::LowerThan(s) => format!("under_{}", format_sats(*s)),
|
|
||||||
AmountFilter::GreaterOrEqual(s) => format!("above_{}", format_sats(*s)),
|
|
||||||
AmountFilter::Range(r) => {
|
|
||||||
format!(
|
|
||||||
"above_{}_under_{}",
|
|
||||||
format_sats(r.start),
|
|
||||||
format_sats(r.end)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_sats(sats: Sats) -> String {
|
|
||||||
match sats {
|
|
||||||
s if s == Sats::ZERO => "0sats".to_string(),
|
|
||||||
s if s == Sats::_1 => "1sat".to_string(),
|
|
||||||
s if s == Sats::_10 => "10sats".to_string(),
|
|
||||||
s if s == Sats::_100 => "100sats".to_string(),
|
|
||||||
s if s == Sats::_1K => "1k_sats".to_string(),
|
|
||||||
s if s == Sats::_10K => "10k_sats".to_string(),
|
|
||||||
s if s == Sats::_100K => "100k_sats".to_string(),
|
|
||||||
s if s == Sats::_1M => "1m_sats".to_string(),
|
|
||||||
s if s == Sats::_10M => "10m_sats".to_string(),
|
|
||||||
s if s == Sats::_1BTC => "1btc".to_string(),
|
|
||||||
s if s == Sats::_10BTC => "10btc".to_string(),
|
|
||||||
s if s == Sats::_100BTC => "100btc".to_string(),
|
|
||||||
s if s == Sats::_1K_BTC => "1k_btc".to_string(),
|
|
||||||
s if s == Sats::_10K_BTC => "10k_btc".to_string(),
|
|
||||||
s if s == Sats::_100K_BTC => "100k_btc".to_string(),
|
|
||||||
_ => format!("{}sats", u64::from(sats)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::{Filter, TimeFilter};
|
use super::{CohortName, Filter, TimeFilter};
|
||||||
|
|
||||||
// Age boundary constants in days
|
// Age boundary constants in days
|
||||||
pub const DAYS_1D: usize = 1;
|
pub const DAYS_1D: usize = 1;
|
||||||
@@ -31,7 +34,85 @@ pub const AGE_BOUNDARIES: [usize; 19] = [
|
|||||||
DAYS_3Y, DAYS_4Y, DAYS_5Y, DAYS_6Y, DAYS_7Y, DAYS_8Y, DAYS_10Y, DAYS_12Y, DAYS_15Y,
|
DAYS_3Y, DAYS_4Y, DAYS_5Y, DAYS_6Y, DAYS_7Y, DAYS_8Y, DAYS_10Y, DAYS_12Y, DAYS_15Y,
|
||||||
];
|
];
|
||||||
|
|
||||||
#[derive(Default, Clone, Traversable)]
|
/// Age range bounds (end = usize::MAX means unbounded)
|
||||||
|
pub const AGE_RANGE_BOUNDS: ByAgeRange<Range<usize>> = ByAgeRange {
|
||||||
|
up_to_1d: 0..DAYS_1D,
|
||||||
|
_1d_to_1w: DAYS_1D..DAYS_1W,
|
||||||
|
_1w_to_1m: DAYS_1W..DAYS_1M,
|
||||||
|
_1m_to_2m: DAYS_1M..DAYS_2M,
|
||||||
|
_2m_to_3m: DAYS_2M..DAYS_3M,
|
||||||
|
_3m_to_4m: DAYS_3M..DAYS_4M,
|
||||||
|
_4m_to_5m: DAYS_4M..DAYS_5M,
|
||||||
|
_5m_to_6m: DAYS_5M..DAYS_6M,
|
||||||
|
_6m_to_1y: DAYS_6M..DAYS_1Y,
|
||||||
|
_1y_to_2y: DAYS_1Y..DAYS_2Y,
|
||||||
|
_2y_to_3y: DAYS_2Y..DAYS_3Y,
|
||||||
|
_3y_to_4y: DAYS_3Y..DAYS_4Y,
|
||||||
|
_4y_to_5y: DAYS_4Y..DAYS_5Y,
|
||||||
|
_5y_to_6y: DAYS_5Y..DAYS_6Y,
|
||||||
|
_6y_to_7y: DAYS_6Y..DAYS_7Y,
|
||||||
|
_7y_to_8y: DAYS_7Y..DAYS_8Y,
|
||||||
|
_8y_to_10y: DAYS_8Y..DAYS_10Y,
|
||||||
|
_10y_to_12y: DAYS_10Y..DAYS_12Y,
|
||||||
|
_12y_to_15y: DAYS_12Y..DAYS_15Y,
|
||||||
|
from_15y: DAYS_15Y..usize::MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Age range filters
|
||||||
|
pub const AGE_RANGE_FILTERS: ByAgeRange<Filter> = ByAgeRange {
|
||||||
|
up_to_1d: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS.up_to_1d)),
|
||||||
|
_1d_to_1w: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._1d_to_1w)),
|
||||||
|
_1w_to_1m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._1w_to_1m)),
|
||||||
|
_1m_to_2m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._1m_to_2m)),
|
||||||
|
_2m_to_3m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._2m_to_3m)),
|
||||||
|
_3m_to_4m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._3m_to_4m)),
|
||||||
|
_4m_to_5m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._4m_to_5m)),
|
||||||
|
_5m_to_6m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._5m_to_6m)),
|
||||||
|
_6m_to_1y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._6m_to_1y)),
|
||||||
|
_1y_to_2y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._1y_to_2y)),
|
||||||
|
_2y_to_3y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._2y_to_3y)),
|
||||||
|
_3y_to_4y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._3y_to_4y)),
|
||||||
|
_4y_to_5y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._4y_to_5y)),
|
||||||
|
_5y_to_6y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._5y_to_6y)),
|
||||||
|
_6y_to_7y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._6y_to_7y)),
|
||||||
|
_7y_to_8y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._7y_to_8y)),
|
||||||
|
_8y_to_10y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._8y_to_10y)),
|
||||||
|
_10y_to_12y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._10y_to_12y)),
|
||||||
|
_12y_to_15y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._12y_to_15y)),
|
||||||
|
from_15y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS.from_15y)),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Age range names
|
||||||
|
pub const AGE_RANGE_NAMES: ByAgeRange<CohortName> = ByAgeRange {
|
||||||
|
up_to_1d: CohortName::new("up_to_1d_old", "<1d", "Up to 1 Day Old"),
|
||||||
|
_1d_to_1w: CohortName::new("at_least_1d_up_to_1w_old", "1d-1w", "1 Day to 1 Week Old"),
|
||||||
|
_1w_to_1m: CohortName::new("at_least_1w_up_to_1m_old", "1w-1m", "1 Week to 1 Month Old"),
|
||||||
|
_1m_to_2m: CohortName::new("at_least_1m_up_to_2m_old", "1m-2m", "1 to 2 Months Old"),
|
||||||
|
_2m_to_3m: CohortName::new("at_least_2m_up_to_3m_old", "2m-3m", "2 to 3 Months Old"),
|
||||||
|
_3m_to_4m: CohortName::new("at_least_3m_up_to_4m_old", "3m-4m", "3 to 4 Months Old"),
|
||||||
|
_4m_to_5m: CohortName::new("at_least_4m_up_to_5m_old", "4m-5m", "4 to 5 Months Old"),
|
||||||
|
_5m_to_6m: CohortName::new("at_least_5m_up_to_6m_old", "5m-6m", "5 to 6 Months Old"),
|
||||||
|
_6m_to_1y: CohortName::new("at_least_6m_up_to_1y_old", "6m-1y", "6 Months to 1 Year Old"),
|
||||||
|
_1y_to_2y: CohortName::new("at_least_1y_up_to_2y_old", "1y-2y", "1 to 2 Years Old"),
|
||||||
|
_2y_to_3y: CohortName::new("at_least_2y_up_to_3y_old", "2y-3y", "2 to 3 Years Old"),
|
||||||
|
_3y_to_4y: CohortName::new("at_least_3y_up_to_4y_old", "3y-4y", "3 to 4 Years Old"),
|
||||||
|
_4y_to_5y: CohortName::new("at_least_4y_up_to_5y_old", "4y-5y", "4 to 5 Years Old"),
|
||||||
|
_5y_to_6y: CohortName::new("at_least_5y_up_to_6y_old", "5y-6y", "5 to 6 Years Old"),
|
||||||
|
_6y_to_7y: CohortName::new("at_least_6y_up_to_7y_old", "6y-7y", "6 to 7 Years Old"),
|
||||||
|
_7y_to_8y: CohortName::new("at_least_7y_up_to_8y_old", "7y-8y", "7 to 8 Years Old"),
|
||||||
|
_8y_to_10y: CohortName::new("at_least_8y_up_to_10y_old", "8y-10y", "8 to 10 Years Old"),
|
||||||
|
_10y_to_12y: CohortName::new("at_least_10y_up_to_12y_old", "10y-12y", "10 to 12 Years Old"),
|
||||||
|
_12y_to_15y: CohortName::new("at_least_12y_up_to_15y_old", "12y-15y", "12 to 15 Years Old"),
|
||||||
|
from_15y: CohortName::new("at_least_15y_old", "15y+", "15+ Years Old"),
|
||||||
|
};
|
||||||
|
|
||||||
|
impl ByAgeRange<CohortName> {
|
||||||
|
pub const fn names() -> &'static Self {
|
||||||
|
&AGE_RANGE_NAMES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Traversable, Serialize)]
|
||||||
pub struct ByAgeRange<T> {
|
pub struct ByAgeRange<T> {
|
||||||
pub up_to_1d: T,
|
pub up_to_1d: T,
|
||||||
pub _1d_to_1w: T,
|
pub _1d_to_1w: T,
|
||||||
@@ -85,32 +166,64 @@ impl<T> ByAgeRange<T> {
|
|||||||
|
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
|
let f = AGE_RANGE_FILTERS;
|
||||||
|
let n = AGE_RANGE_NAMES;
|
||||||
Self {
|
Self {
|
||||||
up_to_1d: create(Filter::Time(TimeFilter::Range(0..DAYS_1D))),
|
up_to_1d: create(f.up_to_1d.clone(), n.up_to_1d.id),
|
||||||
_1d_to_1w: create(Filter::Time(TimeFilter::Range(DAYS_1D..DAYS_1W))),
|
_1d_to_1w: create(f._1d_to_1w.clone(), n._1d_to_1w.id),
|
||||||
_1w_to_1m: create(Filter::Time(TimeFilter::Range(DAYS_1W..DAYS_1M))),
|
_1w_to_1m: create(f._1w_to_1m.clone(), n._1w_to_1m.id),
|
||||||
_1m_to_2m: create(Filter::Time(TimeFilter::Range(DAYS_1M..DAYS_2M))),
|
_1m_to_2m: create(f._1m_to_2m.clone(), n._1m_to_2m.id),
|
||||||
_2m_to_3m: create(Filter::Time(TimeFilter::Range(DAYS_2M..DAYS_3M))),
|
_2m_to_3m: create(f._2m_to_3m.clone(), n._2m_to_3m.id),
|
||||||
_3m_to_4m: create(Filter::Time(TimeFilter::Range(DAYS_3M..DAYS_4M))),
|
_3m_to_4m: create(f._3m_to_4m.clone(), n._3m_to_4m.id),
|
||||||
_4m_to_5m: create(Filter::Time(TimeFilter::Range(DAYS_4M..DAYS_5M))),
|
_4m_to_5m: create(f._4m_to_5m.clone(), n._4m_to_5m.id),
|
||||||
_5m_to_6m: create(Filter::Time(TimeFilter::Range(DAYS_5M..DAYS_6M))),
|
_5m_to_6m: create(f._5m_to_6m.clone(), n._5m_to_6m.id),
|
||||||
_6m_to_1y: create(Filter::Time(TimeFilter::Range(DAYS_6M..DAYS_1Y))),
|
_6m_to_1y: create(f._6m_to_1y.clone(), n._6m_to_1y.id),
|
||||||
_1y_to_2y: create(Filter::Time(TimeFilter::Range(DAYS_1Y..DAYS_2Y))),
|
_1y_to_2y: create(f._1y_to_2y.clone(), n._1y_to_2y.id),
|
||||||
_2y_to_3y: create(Filter::Time(TimeFilter::Range(DAYS_2Y..DAYS_3Y))),
|
_2y_to_3y: create(f._2y_to_3y.clone(), n._2y_to_3y.id),
|
||||||
_3y_to_4y: create(Filter::Time(TimeFilter::Range(DAYS_3Y..DAYS_4Y))),
|
_3y_to_4y: create(f._3y_to_4y.clone(), n._3y_to_4y.id),
|
||||||
_4y_to_5y: create(Filter::Time(TimeFilter::Range(DAYS_4Y..DAYS_5Y))),
|
_4y_to_5y: create(f._4y_to_5y.clone(), n._4y_to_5y.id),
|
||||||
_5y_to_6y: create(Filter::Time(TimeFilter::Range(DAYS_5Y..DAYS_6Y))),
|
_5y_to_6y: create(f._5y_to_6y.clone(), n._5y_to_6y.id),
|
||||||
_6y_to_7y: create(Filter::Time(TimeFilter::Range(DAYS_6Y..DAYS_7Y))),
|
_6y_to_7y: create(f._6y_to_7y.clone(), n._6y_to_7y.id),
|
||||||
_7y_to_8y: create(Filter::Time(TimeFilter::Range(DAYS_7Y..DAYS_8Y))),
|
_7y_to_8y: create(f._7y_to_8y.clone(), n._7y_to_8y.id),
|
||||||
_8y_to_10y: create(Filter::Time(TimeFilter::Range(DAYS_8Y..DAYS_10Y))),
|
_8y_to_10y: create(f._8y_to_10y.clone(), n._8y_to_10y.id),
|
||||||
_10y_to_12y: create(Filter::Time(TimeFilter::Range(DAYS_10Y..DAYS_12Y))),
|
_10y_to_12y: create(f._10y_to_12y.clone(), n._10y_to_12y.id),
|
||||||
_12y_to_15y: create(Filter::Time(TimeFilter::Range(DAYS_12Y..DAYS_15Y))),
|
_12y_to_15y: create(f._12y_to_15y.clone(), n._12y_to_15y.id),
|
||||||
from_15y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_15Y))),
|
from_15y: create(f.from_15y.clone(), n.from_15y.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
let f = AGE_RANGE_FILTERS;
|
||||||
|
let n = AGE_RANGE_NAMES;
|
||||||
|
Ok(Self {
|
||||||
|
up_to_1d: create(f.up_to_1d.clone(), n.up_to_1d.id)?,
|
||||||
|
_1d_to_1w: create(f._1d_to_1w.clone(), n._1d_to_1w.id)?,
|
||||||
|
_1w_to_1m: create(f._1w_to_1m.clone(), n._1w_to_1m.id)?,
|
||||||
|
_1m_to_2m: create(f._1m_to_2m.clone(), n._1m_to_2m.id)?,
|
||||||
|
_2m_to_3m: create(f._2m_to_3m.clone(), n._2m_to_3m.id)?,
|
||||||
|
_3m_to_4m: create(f._3m_to_4m.clone(), n._3m_to_4m.id)?,
|
||||||
|
_4m_to_5m: create(f._4m_to_5m.clone(), n._4m_to_5m.id)?,
|
||||||
|
_5m_to_6m: create(f._5m_to_6m.clone(), n._5m_to_6m.id)?,
|
||||||
|
_6m_to_1y: create(f._6m_to_1y.clone(), n._6m_to_1y.id)?,
|
||||||
|
_1y_to_2y: create(f._1y_to_2y.clone(), n._1y_to_2y.id)?,
|
||||||
|
_2y_to_3y: create(f._2y_to_3y.clone(), n._2y_to_3y.id)?,
|
||||||
|
_3y_to_4y: create(f._3y_to_4y.clone(), n._3y_to_4y.id)?,
|
||||||
|
_4y_to_5y: create(f._4y_to_5y.clone(), n._4y_to_5y.id)?,
|
||||||
|
_5y_to_6y: create(f._5y_to_6y.clone(), n._5y_to_6y.id)?,
|
||||||
|
_6y_to_7y: create(f._6y_to_7y.clone(), n._6y_to_7y.id)?,
|
||||||
|
_7y_to_8y: create(f._7y_to_8y.clone(), n._7y_to_8y.id)?,
|
||||||
|
_8y_to_10y: create(f._8y_to_10y.clone(), n._8y_to_10y.id)?,
|
||||||
|
_10y_to_12y: create(f._10y_to_12y.clone(), n._10y_to_12y.id)?,
|
||||||
|
_12y_to_15y: create(f._12y_to_15y.clone(), n._12y_to_15y.id)?,
|
||||||
|
from_15y: create(f.from_15y.clone(), n.from_15y.id)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
[
|
[
|
||||||
&self.up_to_1d,
|
&self.up_to_1d,
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign, Range};
|
||||||
|
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::Sats;
|
use brk_types::Sats;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::{AmountFilter, Filter};
|
use super::{AmountFilter, CohortName, Filter};
|
||||||
|
|
||||||
/// Bucket index for amount ranges. Use for cheap comparisons and direct lookups.
|
/// Bucket index for amount ranges. Use for cheap comparisons and direct lookups.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@@ -57,7 +58,108 @@ pub fn amounts_in_different_buckets(a: Sats, b: Sats) -> bool {
|
|||||||
AmountBucket::from(a) != AmountBucket::from(b)
|
AmountBucket::from(a) != AmountBucket::from(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Traversable)]
|
/// Amount range bounds
|
||||||
|
pub const AMOUNT_RANGE_BOUNDS: ByAmountRange<Range<Sats>> = ByAmountRange {
|
||||||
|
_0sats: Sats::ZERO..Sats::_1,
|
||||||
|
_1sat_to_10sats: Sats::_1..Sats::_10,
|
||||||
|
_10sats_to_100sats: Sats::_10..Sats::_100,
|
||||||
|
_100sats_to_1k_sats: Sats::_100..Sats::_1K,
|
||||||
|
_1k_sats_to_10k_sats: Sats::_1K..Sats::_10K,
|
||||||
|
_10k_sats_to_100k_sats: Sats::_10K..Sats::_100K,
|
||||||
|
_100k_sats_to_1m_sats: Sats::_100K..Sats::_1M,
|
||||||
|
_1m_sats_to_10m_sats: Sats::_1M..Sats::_10M,
|
||||||
|
_10m_sats_to_1btc: Sats::_10M..Sats::_1BTC,
|
||||||
|
_1btc_to_10btc: Sats::_1BTC..Sats::_10BTC,
|
||||||
|
_10btc_to_100btc: Sats::_10BTC..Sats::_100BTC,
|
||||||
|
_100btc_to_1k_btc: Sats::_100BTC..Sats::_1K_BTC,
|
||||||
|
_1k_btc_to_10k_btc: Sats::_1K_BTC..Sats::_10K_BTC,
|
||||||
|
_10k_btc_to_100k_btc: Sats::_10K_BTC..Sats::_100K_BTC,
|
||||||
|
_100k_btc_or_more: Sats::_100K_BTC..Sats::MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Amount range names
|
||||||
|
pub const AMOUNT_RANGE_NAMES: ByAmountRange<CohortName> = ByAmountRange {
|
||||||
|
_0sats: CohortName::new("with_0sats", "0 sats", "0 Sats"),
|
||||||
|
_1sat_to_10sats: CohortName::new("above_1sat_under_10sats", "1-10 sats", "1 to 10 Sats"),
|
||||||
|
_10sats_to_100sats: CohortName::new(
|
||||||
|
"above_10sats_under_100sats",
|
||||||
|
"10-100 sats",
|
||||||
|
"10 to 100 Sats",
|
||||||
|
),
|
||||||
|
_100sats_to_1k_sats: CohortName::new(
|
||||||
|
"above_100sats_under_1k_sats",
|
||||||
|
"100-1k sats",
|
||||||
|
"100 to 1K Sats",
|
||||||
|
),
|
||||||
|
_1k_sats_to_10k_sats: CohortName::new(
|
||||||
|
"above_1k_sats_under_10k_sats",
|
||||||
|
"1k-10k sats",
|
||||||
|
"1K to 10K Sats",
|
||||||
|
),
|
||||||
|
_10k_sats_to_100k_sats: CohortName::new(
|
||||||
|
"above_10k_sats_under_100k_sats",
|
||||||
|
"10k-100k sats",
|
||||||
|
"10K to 100K Sats",
|
||||||
|
),
|
||||||
|
_100k_sats_to_1m_sats: CohortName::new(
|
||||||
|
"above_100k_sats_under_1m_sats",
|
||||||
|
"100k-1M sats",
|
||||||
|
"100K to 1M Sats",
|
||||||
|
),
|
||||||
|
_1m_sats_to_10m_sats: CohortName::new(
|
||||||
|
"above_1m_sats_under_10m_sats",
|
||||||
|
"1M-10M sats",
|
||||||
|
"1M to 10M Sats",
|
||||||
|
),
|
||||||
|
_10m_sats_to_1btc: CohortName::new("above_10m_sats_under_1btc", "0.1-1 BTC", "0.1 to 1 BTC"),
|
||||||
|
_1btc_to_10btc: CohortName::new("above_1btc_under_10btc", "1-10 BTC", "1 to 10 BTC"),
|
||||||
|
_10btc_to_100btc: CohortName::new("above_10btc_under_100btc", "10-100 BTC", "10 to 100 BTC"),
|
||||||
|
_100btc_to_1k_btc: CohortName::new("above_100btc_under_1k_btc", "100-1k BTC", "100 to 1K BTC"),
|
||||||
|
_1k_btc_to_10k_btc: CohortName::new(
|
||||||
|
"above_1k_btc_under_10k_btc",
|
||||||
|
"1k-10k BTC",
|
||||||
|
"1K to 10K BTC",
|
||||||
|
),
|
||||||
|
_10k_btc_to_100k_btc: CohortName::new(
|
||||||
|
"above_10k_btc_under_100k_btc",
|
||||||
|
"10k-100k BTC",
|
||||||
|
"10K to 100K BTC",
|
||||||
|
),
|
||||||
|
_100k_btc_or_more: CohortName::new("above_100k_btc", "100k+ BTC", "100K+ BTC"),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Amount range filters
|
||||||
|
pub const AMOUNT_RANGE_FILTERS: ByAmountRange<Filter> = ByAmountRange {
|
||||||
|
_0sats: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._0sats)),
|
||||||
|
_1sat_to_10sats: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._1sat_to_10sats)),
|
||||||
|
_10sats_to_100sats: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._10sats_to_100sats)),
|
||||||
|
_100sats_to_1k_sats: Filter::Amount(AmountFilter::Range(
|
||||||
|
AMOUNT_RANGE_BOUNDS._100sats_to_1k_sats,
|
||||||
|
)),
|
||||||
|
_1k_sats_to_10k_sats: Filter::Amount(AmountFilter::Range(
|
||||||
|
AMOUNT_RANGE_BOUNDS._1k_sats_to_10k_sats,
|
||||||
|
)),
|
||||||
|
_10k_sats_to_100k_sats: Filter::Amount(AmountFilter::Range(
|
||||||
|
AMOUNT_RANGE_BOUNDS._10k_sats_to_100k_sats,
|
||||||
|
)),
|
||||||
|
_100k_sats_to_1m_sats: Filter::Amount(AmountFilter::Range(
|
||||||
|
AMOUNT_RANGE_BOUNDS._100k_sats_to_1m_sats,
|
||||||
|
)),
|
||||||
|
_1m_sats_to_10m_sats: Filter::Amount(AmountFilter::Range(
|
||||||
|
AMOUNT_RANGE_BOUNDS._1m_sats_to_10m_sats,
|
||||||
|
)),
|
||||||
|
_10m_sats_to_1btc: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._10m_sats_to_1btc)),
|
||||||
|
_1btc_to_10btc: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._1btc_to_10btc)),
|
||||||
|
_10btc_to_100btc: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._10btc_to_100btc)),
|
||||||
|
_100btc_to_1k_btc: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._100btc_to_1k_btc)),
|
||||||
|
_1k_btc_to_10k_btc: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._1k_btc_to_10k_btc)),
|
||||||
|
_10k_btc_to_100k_btc: Filter::Amount(AmountFilter::Range(
|
||||||
|
AMOUNT_RANGE_BOUNDS._10k_btc_to_100k_btc,
|
||||||
|
)),
|
||||||
|
_100k_btc_or_more: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._100k_btc_or_more)),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Traversable, Serialize)]
|
||||||
pub struct ByAmountRange<T> {
|
pub struct ByAmountRange<T> {
|
||||||
pub _0sats: T,
|
pub _0sats: T,
|
||||||
pub _1sat_to_10sats: T,
|
pub _1sat_to_10sats: T,
|
||||||
@@ -76,50 +178,84 @@ pub struct ByAmountRange<T> {
|
|||||||
pub _100k_btc_or_more: T,
|
pub _100k_btc_or_more: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ByAmountRange<CohortName> {
|
||||||
|
pub const fn names() -> &'static Self {
|
||||||
|
&AMOUNT_RANGE_NAMES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ByAmountRange<T> {
|
impl<T> ByAmountRange<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
|
let f = AMOUNT_RANGE_FILTERS;
|
||||||
|
let n = AMOUNT_RANGE_NAMES;
|
||||||
Self {
|
Self {
|
||||||
_0sats: create(Filter::Amount(AmountFilter::Range(Sats::ZERO..Sats::_1))),
|
_0sats: create(f._0sats.clone(), n._0sats.id),
|
||||||
_1sat_to_10sats: create(Filter::Amount(AmountFilter::Range(Sats::_1..Sats::_10))),
|
_1sat_to_10sats: create(f._1sat_to_10sats.clone(), n._1sat_to_10sats.id),
|
||||||
_10sats_to_100sats: create(Filter::Amount(AmountFilter::Range(Sats::_10..Sats::_100))),
|
_10sats_to_100sats: create(f._10sats_to_100sats.clone(), n._10sats_to_100sats.id),
|
||||||
_100sats_to_1k_sats: create(Filter::Amount(AmountFilter::Range(Sats::_100..Sats::_1K))),
|
_100sats_to_1k_sats: create(f._100sats_to_1k_sats.clone(), n._100sats_to_1k_sats.id),
|
||||||
_1k_sats_to_10k_sats: create(Filter::Amount(AmountFilter::Range(
|
_1k_sats_to_10k_sats: create(f._1k_sats_to_10k_sats.clone(), n._1k_sats_to_10k_sats.id),
|
||||||
Sats::_1K..Sats::_10K,
|
_10k_sats_to_100k_sats: create(
|
||||||
))),
|
f._10k_sats_to_100k_sats.clone(),
|
||||||
_10k_sats_to_100k_sats: create(Filter::Amount(AmountFilter::Range(
|
n._10k_sats_to_100k_sats.id,
|
||||||
Sats::_10K..Sats::_100K,
|
),
|
||||||
))),
|
_100k_sats_to_1m_sats: create(
|
||||||
_100k_sats_to_1m_sats: create(Filter::Amount(AmountFilter::Range(
|
f._100k_sats_to_1m_sats.clone(),
|
||||||
Sats::_100K..Sats::_1M,
|
n._100k_sats_to_1m_sats.id,
|
||||||
))),
|
),
|
||||||
_1m_sats_to_10m_sats: create(Filter::Amount(AmountFilter::Range(
|
_1m_sats_to_10m_sats: create(f._1m_sats_to_10m_sats.clone(), n._1m_sats_to_10m_sats.id),
|
||||||
Sats::_1M..Sats::_10M,
|
_10m_sats_to_1btc: create(f._10m_sats_to_1btc.clone(), n._10m_sats_to_1btc.id),
|
||||||
))),
|
_1btc_to_10btc: create(f._1btc_to_10btc.clone(), n._1btc_to_10btc.id),
|
||||||
_10m_sats_to_1btc: create(Filter::Amount(AmountFilter::Range(Sats::_10M..Sats::_1BTC))),
|
_10btc_to_100btc: create(f._10btc_to_100btc.clone(), n._10btc_to_100btc.id),
|
||||||
_1btc_to_10btc: create(Filter::Amount(AmountFilter::Range(
|
_100btc_to_1k_btc: create(f._100btc_to_1k_btc.clone(), n._100btc_to_1k_btc.id),
|
||||||
Sats::_1BTC..Sats::_10BTC,
|
_1k_btc_to_10k_btc: create(f._1k_btc_to_10k_btc.clone(), n._1k_btc_to_10k_btc.id),
|
||||||
))),
|
_10k_btc_to_100k_btc: create(f._10k_btc_to_100k_btc.clone(), n._10k_btc_to_100k_btc.id),
|
||||||
_10btc_to_100btc: create(Filter::Amount(AmountFilter::Range(
|
_100k_btc_or_more: create(f._100k_btc_or_more.clone(), n._100k_btc_or_more.id),
|
||||||
Sats::_10BTC..Sats::_100BTC,
|
|
||||||
))),
|
|
||||||
_100btc_to_1k_btc: create(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_100BTC..Sats::_1K_BTC,
|
|
||||||
))),
|
|
||||||
_1k_btc_to_10k_btc: create(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_1K_BTC..Sats::_10K_BTC,
|
|
||||||
))),
|
|
||||||
_10k_btc_to_100k_btc: create(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_10K_BTC..Sats::_100K_BTC,
|
|
||||||
))),
|
|
||||||
_100k_btc_or_more: create(Filter::Amount(AmountFilter::Range(
|
|
||||||
Sats::_100K_BTC..Sats::MAX,
|
|
||||||
))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
let f = AMOUNT_RANGE_FILTERS;
|
||||||
|
let n = AMOUNT_RANGE_NAMES;
|
||||||
|
Ok(Self {
|
||||||
|
_0sats: create(f._0sats.clone(), n._0sats.id)?,
|
||||||
|
_1sat_to_10sats: create(f._1sat_to_10sats.clone(), n._1sat_to_10sats.id)?,
|
||||||
|
_10sats_to_100sats: create(f._10sats_to_100sats.clone(), n._10sats_to_100sats.id)?,
|
||||||
|
_100sats_to_1k_sats: create(f._100sats_to_1k_sats.clone(), n._100sats_to_1k_sats.id)?,
|
||||||
|
_1k_sats_to_10k_sats: create(
|
||||||
|
f._1k_sats_to_10k_sats.clone(),
|
||||||
|
n._1k_sats_to_10k_sats.id,
|
||||||
|
)?,
|
||||||
|
_10k_sats_to_100k_sats: create(
|
||||||
|
f._10k_sats_to_100k_sats.clone(),
|
||||||
|
n._10k_sats_to_100k_sats.id,
|
||||||
|
)?,
|
||||||
|
_100k_sats_to_1m_sats: create(
|
||||||
|
f._100k_sats_to_1m_sats.clone(),
|
||||||
|
n._100k_sats_to_1m_sats.id,
|
||||||
|
)?,
|
||||||
|
_1m_sats_to_10m_sats: create(
|
||||||
|
f._1m_sats_to_10m_sats.clone(),
|
||||||
|
n._1m_sats_to_10m_sats.id,
|
||||||
|
)?,
|
||||||
|
_10m_sats_to_1btc: create(f._10m_sats_to_1btc.clone(), n._10m_sats_to_1btc.id)?,
|
||||||
|
_1btc_to_10btc: create(f._1btc_to_10btc.clone(), n._1btc_to_10btc.id)?,
|
||||||
|
_10btc_to_100btc: create(f._10btc_to_100btc.clone(), n._10btc_to_100btc.id)?,
|
||||||
|
_100btc_to_1k_btc: create(f._100btc_to_1k_btc.clone(), n._100btc_to_1k_btc.id)?,
|
||||||
|
_1k_btc_to_10k_btc: create(f._1k_btc_to_10k_btc.clone(), n._1k_btc_to_10k_btc.id)?,
|
||||||
|
_10k_btc_to_100k_btc: create(
|
||||||
|
f._10k_btc_to_100k_btc.clone(),
|
||||||
|
n._10k_btc_to_100k_btc.id,
|
||||||
|
)?,
|
||||||
|
_100k_btc_or_more: create(f._100k_btc_or_more.clone(), n._100k_btc_or_more.id)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get(&self, value: Sats) -> &T {
|
pub fn get(&self, value: Sats) -> &T {
|
||||||
match AmountBucket::from(value).0 {
|
match AmountBucket::from(value).0 {
|
||||||
|
|||||||
@@ -1,10 +1,38 @@
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{HalvingEpoch, Height};
|
use brk_types::{HalvingEpoch, Height};
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::Filter;
|
use super::{CohortName, Filter};
|
||||||
|
|
||||||
#[derive(Default, Clone, Traversable)]
|
/// Epoch values
|
||||||
|
pub const EPOCH_VALUES: ByEpoch<HalvingEpoch> = ByEpoch {
|
||||||
|
_0: HalvingEpoch::new(0),
|
||||||
|
_1: HalvingEpoch::new(1),
|
||||||
|
_2: HalvingEpoch::new(2),
|
||||||
|
_3: HalvingEpoch::new(3),
|
||||||
|
_4: HalvingEpoch::new(4),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Epoch filters
|
||||||
|
pub const EPOCH_FILTERS: ByEpoch<Filter> = ByEpoch {
|
||||||
|
_0: Filter::Epoch(EPOCH_VALUES._0),
|
||||||
|
_1: Filter::Epoch(EPOCH_VALUES._1),
|
||||||
|
_2: Filter::Epoch(EPOCH_VALUES._2),
|
||||||
|
_3: Filter::Epoch(EPOCH_VALUES._3),
|
||||||
|
_4: Filter::Epoch(EPOCH_VALUES._4),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Epoch names
|
||||||
|
pub const EPOCH_NAMES: ByEpoch<CohortName> = ByEpoch {
|
||||||
|
_0: CohortName::new("epoch_0", "Epoch 0", "Epoch 0"),
|
||||||
|
_1: CohortName::new("epoch_1", "Epoch 1", "Epoch 1"),
|
||||||
|
_2: CohortName::new("epoch_2", "Epoch 2", "Epoch 2"),
|
||||||
|
_3: CohortName::new("epoch_3", "Epoch 3", "Epoch 3"),
|
||||||
|
_4: CohortName::new("epoch_4", "Epoch 4", "Epoch 4"),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Traversable, Serialize)]
|
||||||
pub struct ByEpoch<T> {
|
pub struct ByEpoch<T> {
|
||||||
pub _0: T,
|
pub _0: T,
|
||||||
pub _1: T,
|
pub _1: T,
|
||||||
@@ -13,20 +41,43 @@ pub struct ByEpoch<T> {
|
|||||||
pub _4: T,
|
pub _4: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ByEpoch<CohortName> {
|
||||||
|
pub const fn names() -> &'static Self {
|
||||||
|
&EPOCH_NAMES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ByEpoch<T> {
|
impl<T> ByEpoch<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
|
let f = EPOCH_FILTERS;
|
||||||
|
let n = EPOCH_NAMES;
|
||||||
Self {
|
Self {
|
||||||
_0: create(Filter::Epoch(HalvingEpoch::new(0))),
|
_0: create(f._0, n._0.id),
|
||||||
_1: create(Filter::Epoch(HalvingEpoch::new(1))),
|
_1: create(f._1, n._1.id),
|
||||||
_2: create(Filter::Epoch(HalvingEpoch::new(2))),
|
_2: create(f._2, n._2.id),
|
||||||
_3: create(Filter::Epoch(HalvingEpoch::new(3))),
|
_3: create(f._3, n._3.id),
|
||||||
_4: create(Filter::Epoch(HalvingEpoch::new(4))),
|
_4: create(f._4, n._4.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
let f = EPOCH_FILTERS;
|
||||||
|
let n = EPOCH_NAMES;
|
||||||
|
Ok(Self {
|
||||||
|
_0: create(f._0, n._0.id)?,
|
||||||
|
_1: create(f._1, n._1.id)?,
|
||||||
|
_2: create(f._2, n._2.id)?,
|
||||||
|
_3: create(f._3, n._3.id)?,
|
||||||
|
_4: create(f._4, n._4.id)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
[&self._0, &self._1, &self._2, &self._3, &self._4].into_iter()
|
[&self._0, &self._1, &self._2, &self._3, &self._4].into_iter()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,64 @@
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::Sats;
|
use brk_types::Sats;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::{AmountFilter, Filter};
|
use super::{AmountFilter, CohortName, Filter};
|
||||||
|
|
||||||
#[derive(Default, Clone, Traversable)]
|
/// Greater-or-equal amount thresholds
|
||||||
|
pub const GE_AMOUNT_THRESHOLDS: ByGreatEqualAmount<Sats> = ByGreatEqualAmount {
|
||||||
|
_1sat: Sats::_1,
|
||||||
|
_10sats: Sats::_10,
|
||||||
|
_100sats: Sats::_100,
|
||||||
|
_1k_sats: Sats::_1K,
|
||||||
|
_10k_sats: Sats::_10K,
|
||||||
|
_100k_sats: Sats::_100K,
|
||||||
|
_1m_sats: Sats::_1M,
|
||||||
|
_10m_sats: Sats::_10M,
|
||||||
|
_1btc: Sats::_1BTC,
|
||||||
|
_10btc: Sats::_10BTC,
|
||||||
|
_100btc: Sats::_100BTC,
|
||||||
|
_1k_btc: Sats::_1K_BTC,
|
||||||
|
_10k_btc: Sats::_10K_BTC,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Greater-or-equal amount names
|
||||||
|
pub const GE_AMOUNT_NAMES: ByGreatEqualAmount<CohortName> = ByGreatEqualAmount {
|
||||||
|
_1sat: CohortName::new("above_1sat", "1+ sats", "Above 1 Sat"),
|
||||||
|
_10sats: CohortName::new("above_10sats", "10+ sats", "Above 10 Sats"),
|
||||||
|
_100sats: CohortName::new("above_100sats", "100+ sats", "Above 100 Sats"),
|
||||||
|
_1k_sats: CohortName::new("above_1k_sats", "1k+ sats", "Above 1K Sats"),
|
||||||
|
_10k_sats: CohortName::new("above_10k_sats", "10k+ sats", "Above 10K Sats"),
|
||||||
|
_100k_sats: CohortName::new("above_100k_sats", "100k+ sats", "Above 100K Sats"),
|
||||||
|
_1m_sats: CohortName::new("above_1m_sats", "1M+ sats", "Above 1M Sats"),
|
||||||
|
_10m_sats: CohortName::new("above_10m_sats", "0.1+ BTC", "Above 0.1 BTC"),
|
||||||
|
_1btc: CohortName::new("above_1btc", "1+ BTC", "Above 1 BTC"),
|
||||||
|
_10btc: CohortName::new("above_10btc", "10+ BTC", "Above 10 BTC"),
|
||||||
|
_100btc: CohortName::new("above_100btc", "100+ BTC", "Above 100 BTC"),
|
||||||
|
_1k_btc: CohortName::new("above_1k_btc", "1k+ BTC", "Above 1K BTC"),
|
||||||
|
_10k_btc: CohortName::new("above_10k_btc", "10k+ BTC", "Above 10K BTC"),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Greater-or-equal amount filters
|
||||||
|
pub const GE_AMOUNT_FILTERS: ByGreatEqualAmount<Filter> = ByGreatEqualAmount {
|
||||||
|
_1sat: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._1sat)),
|
||||||
|
_10sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._10sats)),
|
||||||
|
_100sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._100sats)),
|
||||||
|
_1k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._1k_sats)),
|
||||||
|
_10k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._10k_sats)),
|
||||||
|
_100k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||||
|
GE_AMOUNT_THRESHOLDS._100k_sats,
|
||||||
|
)),
|
||||||
|
_1m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._1m_sats)),
|
||||||
|
_10m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._10m_sats)),
|
||||||
|
_1btc: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._1btc)),
|
||||||
|
_10btc: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._10btc)),
|
||||||
|
_100btc: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._100btc)),
|
||||||
|
_1k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._1k_btc)),
|
||||||
|
_10k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._10k_btc)),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Traversable, Serialize)]
|
||||||
pub struct ByGreatEqualAmount<T> {
|
pub struct ByGreatEqualAmount<T> {
|
||||||
pub _1sat: T,
|
pub _1sat: T,
|
||||||
pub _10sats: T,
|
pub _10sats: T,
|
||||||
@@ -21,28 +75,59 @@ pub struct ByGreatEqualAmount<T> {
|
|||||||
pub _10k_btc: T,
|
pub _10k_btc: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ByGreatEqualAmount<CohortName> {
|
||||||
|
pub const fn names() -> &'static Self {
|
||||||
|
&GE_AMOUNT_NAMES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ByGreatEqualAmount<T> {
|
impl<T> ByGreatEqualAmount<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
|
let f = GE_AMOUNT_FILTERS;
|
||||||
|
let n = GE_AMOUNT_NAMES;
|
||||||
Self {
|
Self {
|
||||||
_1sat: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1))),
|
_1sat: create(f._1sat.clone(), n._1sat.id),
|
||||||
_10sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10))),
|
_10sats: create(f._10sats.clone(), n._10sats.id),
|
||||||
_100sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100))),
|
_100sats: create(f._100sats.clone(), n._100sats.id),
|
||||||
_1k_sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K))),
|
_1k_sats: create(f._1k_sats.clone(), n._1k_sats.id),
|
||||||
_10k_sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K))),
|
_10k_sats: create(f._10k_sats.clone(), n._10k_sats.id),
|
||||||
_100k_sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100K))),
|
_100k_sats: create(f._100k_sats.clone(), n._100k_sats.id),
|
||||||
_1m_sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1M))),
|
_1m_sats: create(f._1m_sats.clone(), n._1m_sats.id),
|
||||||
_10m_sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10M))),
|
_10m_sats: create(f._10m_sats.clone(), n._10m_sats.id),
|
||||||
_1btc: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1BTC))),
|
_1btc: create(f._1btc.clone(), n._1btc.id),
|
||||||
_10btc: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10BTC))),
|
_10btc: create(f._10btc.clone(), n._10btc.id),
|
||||||
_100btc: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100BTC))),
|
_100btc: create(f._100btc.clone(), n._100btc.id),
|
||||||
_1k_btc: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K_BTC))),
|
_1k_btc: create(f._1k_btc.clone(), n._1k_btc.id),
|
||||||
_10k_btc: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K_BTC))),
|
_10k_btc: create(f._10k_btc.clone(), n._10k_btc.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
let f = GE_AMOUNT_FILTERS;
|
||||||
|
let n = GE_AMOUNT_NAMES;
|
||||||
|
Ok(Self {
|
||||||
|
_1sat: create(f._1sat.clone(), n._1sat.id)?,
|
||||||
|
_10sats: create(f._10sats.clone(), n._10sats.id)?,
|
||||||
|
_100sats: create(f._100sats.clone(), n._100sats.id)?,
|
||||||
|
_1k_sats: create(f._1k_sats.clone(), n._1k_sats.id)?,
|
||||||
|
_10k_sats: create(f._10k_sats.clone(), n._10k_sats.id)?,
|
||||||
|
_100k_sats: create(f._100k_sats.clone(), n._100k_sats.id)?,
|
||||||
|
_1m_sats: create(f._1m_sats.clone(), n._1m_sats.id)?,
|
||||||
|
_10m_sats: create(f._10m_sats.clone(), n._10m_sats.id)?,
|
||||||
|
_1btc: create(f._1btc.clone(), n._1btc.id)?,
|
||||||
|
_10btc: create(f._10btc.clone(), n._10btc.id)?,
|
||||||
|
_100btc: create(f._100btc.clone(), n._100btc.id)?,
|
||||||
|
_1k_btc: create(f._1k_btc.clone(), n._1k_btc.id)?,
|
||||||
|
_10k_btc: create(f._10k_btc.clone(), n._10k_btc.id)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
[
|
[
|
||||||
&self._1sat,
|
&self._1sat,
|
||||||
|
|||||||
@@ -1,10 +1,62 @@
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::Sats;
|
use brk_types::Sats;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::{AmountFilter, Filter};
|
use super::{AmountFilter, CohortName, Filter};
|
||||||
|
|
||||||
#[derive(Default, Clone, Traversable)]
|
/// Lower-than amount thresholds
|
||||||
|
pub const LT_AMOUNT_THRESHOLDS: ByLowerThanAmount<Sats> = ByLowerThanAmount {
|
||||||
|
_10sats: Sats::_10,
|
||||||
|
_100sats: Sats::_100,
|
||||||
|
_1k_sats: Sats::_1K,
|
||||||
|
_10k_sats: Sats::_10K,
|
||||||
|
_100k_sats: Sats::_100K,
|
||||||
|
_1m_sats: Sats::_1M,
|
||||||
|
_10m_sats: Sats::_10M,
|
||||||
|
_1btc: Sats::_1BTC,
|
||||||
|
_10btc: Sats::_10BTC,
|
||||||
|
_100btc: Sats::_100BTC,
|
||||||
|
_1k_btc: Sats::_1K_BTC,
|
||||||
|
_10k_btc: Sats::_10K_BTC,
|
||||||
|
_100k_btc: Sats::_100K_BTC,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Lower-than amount names
|
||||||
|
pub const LT_AMOUNT_NAMES: ByLowerThanAmount<CohortName> = ByLowerThanAmount {
|
||||||
|
_10sats: CohortName::new("under_10sats", "<10 sats", "Under 10 Sats"),
|
||||||
|
_100sats: CohortName::new("under_100sats", "<100 sats", "Under 100 Sats"),
|
||||||
|
_1k_sats: CohortName::new("under_1k_sats", "<1k sats", "Under 1K Sats"),
|
||||||
|
_10k_sats: CohortName::new("under_10k_sats", "<10k sats", "Under 10K Sats"),
|
||||||
|
_100k_sats: CohortName::new("under_100k_sats", "<100k sats", "Under 100K Sats"),
|
||||||
|
_1m_sats: CohortName::new("under_1m_sats", "<1M sats", "Under 1M Sats"),
|
||||||
|
_10m_sats: CohortName::new("under_10m_sats", "<0.1 BTC", "Under 0.1 BTC"),
|
||||||
|
_1btc: CohortName::new("under_1btc", "<1 BTC", "Under 1 BTC"),
|
||||||
|
_10btc: CohortName::new("under_10btc", "<10 BTC", "Under 10 BTC"),
|
||||||
|
_100btc: CohortName::new("under_100btc", "<100 BTC", "Under 100 BTC"),
|
||||||
|
_1k_btc: CohortName::new("under_1k_btc", "<1k BTC", "Under 1K BTC"),
|
||||||
|
_10k_btc: CohortName::new("under_10k_btc", "<10k BTC", "Under 10K BTC"),
|
||||||
|
_100k_btc: CohortName::new("under_100k_btc", "<100k BTC", "Under 100K BTC"),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Lower-than amount filters
|
||||||
|
pub const LT_AMOUNT_FILTERS: ByLowerThanAmount<Filter> = ByLowerThanAmount {
|
||||||
|
_10sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._10sats)),
|
||||||
|
_100sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._100sats)),
|
||||||
|
_1k_sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._1k_sats)),
|
||||||
|
_10k_sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._10k_sats)),
|
||||||
|
_100k_sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._100k_sats)),
|
||||||
|
_1m_sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._1m_sats)),
|
||||||
|
_10m_sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._10m_sats)),
|
||||||
|
_1btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._1btc)),
|
||||||
|
_10btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._10btc)),
|
||||||
|
_100btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._100btc)),
|
||||||
|
_1k_btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._1k_btc)),
|
||||||
|
_10k_btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._10k_btc)),
|
||||||
|
_100k_btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._100k_btc)),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Traversable, Serialize)]
|
||||||
pub struct ByLowerThanAmount<T> {
|
pub struct ByLowerThanAmount<T> {
|
||||||
pub _10sats: T,
|
pub _10sats: T,
|
||||||
pub _100sats: T,
|
pub _100sats: T,
|
||||||
@@ -21,28 +73,59 @@ pub struct ByLowerThanAmount<T> {
|
|||||||
pub _100k_btc: T,
|
pub _100k_btc: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ByLowerThanAmount<CohortName> {
|
||||||
|
pub const fn names() -> &'static Self {
|
||||||
|
<_AMOUNT_NAMES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ByLowerThanAmount<T> {
|
impl<T> ByLowerThanAmount<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
|
let f = LT_AMOUNT_FILTERS;
|
||||||
|
let n = LT_AMOUNT_NAMES;
|
||||||
Self {
|
Self {
|
||||||
_10sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_10))),
|
_10sats: create(f._10sats.clone(), n._10sats.id),
|
||||||
_100sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_100))),
|
_100sats: create(f._100sats.clone(), n._100sats.id),
|
||||||
_1k_sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_1K))),
|
_1k_sats: create(f._1k_sats.clone(), n._1k_sats.id),
|
||||||
_10k_sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_10K))),
|
_10k_sats: create(f._10k_sats.clone(), n._10k_sats.id),
|
||||||
_100k_sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_100K))),
|
_100k_sats: create(f._100k_sats.clone(), n._100k_sats.id),
|
||||||
_1m_sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_1M))),
|
_1m_sats: create(f._1m_sats.clone(), n._1m_sats.id),
|
||||||
_10m_sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_10M))),
|
_10m_sats: create(f._10m_sats.clone(), n._10m_sats.id),
|
||||||
_1btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_1BTC))),
|
_1btc: create(f._1btc.clone(), n._1btc.id),
|
||||||
_10btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_10BTC))),
|
_10btc: create(f._10btc.clone(), n._10btc.id),
|
||||||
_100btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_100BTC))),
|
_100btc: create(f._100btc.clone(), n._100btc.id),
|
||||||
_1k_btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_1K_BTC))),
|
_1k_btc: create(f._1k_btc.clone(), n._1k_btc.id),
|
||||||
_10k_btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_10K_BTC))),
|
_10k_btc: create(f._10k_btc.clone(), n._10k_btc.id),
|
||||||
_100k_btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_100K_BTC))),
|
_100k_btc: create(f._100k_btc.clone(), n._100k_btc.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
let f = LT_AMOUNT_FILTERS;
|
||||||
|
let n = LT_AMOUNT_NAMES;
|
||||||
|
Ok(Self {
|
||||||
|
_10sats: create(f._10sats.clone(), n._10sats.id)?,
|
||||||
|
_100sats: create(f._100sats.clone(), n._100sats.id)?,
|
||||||
|
_1k_sats: create(f._1k_sats.clone(), n._1k_sats.id)?,
|
||||||
|
_10k_sats: create(f._10k_sats.clone(), n._10k_sats.id)?,
|
||||||
|
_100k_sats: create(f._100k_sats.clone(), n._100k_sats.id)?,
|
||||||
|
_1m_sats: create(f._1m_sats.clone(), n._1m_sats.id)?,
|
||||||
|
_10m_sats: create(f._10m_sats.clone(), n._10m_sats.id)?,
|
||||||
|
_1btc: create(f._1btc.clone(), n._1btc.id)?,
|
||||||
|
_10btc: create(f._10btc.clone(), n._10btc.id)?,
|
||||||
|
_100btc: create(f._100btc.clone(), n._100btc.id)?,
|
||||||
|
_1k_btc: create(f._1k_btc.clone(), n._1k_btc.id)?,
|
||||||
|
_10k_btc: create(f._10k_btc.clone(), n._10k_btc.id)?,
|
||||||
|
_100k_btc: create(f._100k_btc.clone(), n._100k_btc.id)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
[
|
[
|
||||||
&self._10sats,
|
&self._10sats,
|
||||||
|
|||||||
@@ -1,12 +1,80 @@
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Filter, TimeFilter, DAYS_10Y, DAYS_12Y, DAYS_15Y, DAYS_1M, DAYS_1W, DAYS_1Y, DAYS_2M, DAYS_2Y,
|
CohortName, Filter, TimeFilter, DAYS_10Y, DAYS_12Y, DAYS_15Y, DAYS_1M, DAYS_1W, DAYS_1Y,
|
||||||
DAYS_3M, DAYS_3Y, DAYS_4M, DAYS_4Y, DAYS_5M, DAYS_5Y, DAYS_6M, DAYS_6Y, DAYS_7Y, DAYS_8Y,
|
DAYS_2M, DAYS_2Y, DAYS_3M, DAYS_3Y, DAYS_4M, DAYS_4Y, DAYS_5M, DAYS_5Y, DAYS_6M, DAYS_6Y,
|
||||||
|
DAYS_7Y, DAYS_8Y,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Clone, Traversable)]
|
/// Max age thresholds in days
|
||||||
|
pub const MAX_AGE_DAYS: ByMaxAge<usize> = ByMaxAge {
|
||||||
|
_1w: DAYS_1W,
|
||||||
|
_1m: DAYS_1M,
|
||||||
|
_2m: DAYS_2M,
|
||||||
|
_3m: DAYS_3M,
|
||||||
|
_4m: DAYS_4M,
|
||||||
|
_5m: DAYS_5M,
|
||||||
|
_6m: DAYS_6M,
|
||||||
|
_1y: DAYS_1Y,
|
||||||
|
_2y: DAYS_2Y,
|
||||||
|
_3y: DAYS_3Y,
|
||||||
|
_4y: DAYS_4Y,
|
||||||
|
_5y: DAYS_5Y,
|
||||||
|
_6y: DAYS_6Y,
|
||||||
|
_7y: DAYS_7Y,
|
||||||
|
_8y: DAYS_8Y,
|
||||||
|
_10y: DAYS_10Y,
|
||||||
|
_12y: DAYS_12Y,
|
||||||
|
_15y: DAYS_15Y,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Max age filters (LowerThan threshold)
|
||||||
|
pub const MAX_AGE_FILTERS: ByMaxAge<Filter> = ByMaxAge {
|
||||||
|
_1w: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._1w)),
|
||||||
|
_1m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._1m)),
|
||||||
|
_2m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._2m)),
|
||||||
|
_3m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._3m)),
|
||||||
|
_4m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._4m)),
|
||||||
|
_5m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._5m)),
|
||||||
|
_6m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._6m)),
|
||||||
|
_1y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._1y)),
|
||||||
|
_2y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._2y)),
|
||||||
|
_3y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._3y)),
|
||||||
|
_4y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._4y)),
|
||||||
|
_5y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._5y)),
|
||||||
|
_6y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._6y)),
|
||||||
|
_7y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._7y)),
|
||||||
|
_8y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._8y)),
|
||||||
|
_10y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._10y)),
|
||||||
|
_12y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._12y)),
|
||||||
|
_15y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._15y)),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Max age names
|
||||||
|
pub const MAX_AGE_NAMES: ByMaxAge<CohortName> = ByMaxAge {
|
||||||
|
_1w: CohortName::new("up_to_1w_old", "<1w", "Up to 1 Week Old"),
|
||||||
|
_1m: CohortName::new("up_to_1m_old", "<1m", "Up to 1 Month Old"),
|
||||||
|
_2m: CohortName::new("up_to_2m_old", "<2m", "Up to 2 Months Old"),
|
||||||
|
_3m: CohortName::new("up_to_3m_old", "<3m", "Up to 3 Months Old"),
|
||||||
|
_4m: CohortName::new("up_to_4m_old", "<4m", "Up to 4 Months Old"),
|
||||||
|
_5m: CohortName::new("up_to_5m_old", "<5m", "Up to 5 Months Old"),
|
||||||
|
_6m: CohortName::new("up_to_6m_old", "<6m", "Up to 6 Months Old"),
|
||||||
|
_1y: CohortName::new("up_to_1y_old", "<1y", "Up to 1 Year Old"),
|
||||||
|
_2y: CohortName::new("up_to_2y_old", "<2y", "Up to 2 Years Old"),
|
||||||
|
_3y: CohortName::new("up_to_3y_old", "<3y", "Up to 3 Years Old"),
|
||||||
|
_4y: CohortName::new("up_to_4y_old", "<4y", "Up to 4 Years Old"),
|
||||||
|
_5y: CohortName::new("up_to_5y_old", "<5y", "Up to 5 Years Old"),
|
||||||
|
_6y: CohortName::new("up_to_6y_old", "<6y", "Up to 6 Years Old"),
|
||||||
|
_7y: CohortName::new("up_to_7y_old", "<7y", "Up to 7 Years Old"),
|
||||||
|
_8y: CohortName::new("up_to_8y_old", "<8y", "Up to 8 Years Old"),
|
||||||
|
_10y: CohortName::new("up_to_10y_old", "<10y", "Up to 10 Years Old"),
|
||||||
|
_12y: CohortName::new("up_to_12y_old", "<12y", "Up to 12 Years Old"),
|
||||||
|
_15y: CohortName::new("up_to_15y_old", "<15y", "Up to 15 Years Old"),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Traversable, Serialize)]
|
||||||
pub struct ByMaxAge<T> {
|
pub struct ByMaxAge<T> {
|
||||||
pub _1w: T,
|
pub _1w: T,
|
||||||
pub _1m: T,
|
pub _1m: T,
|
||||||
@@ -28,33 +96,69 @@ pub struct ByMaxAge<T> {
|
|||||||
pub _15y: T,
|
pub _15y: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ByMaxAge<CohortName> {
|
||||||
|
pub const fn names() -> &'static Self {
|
||||||
|
&MAX_AGE_NAMES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ByMaxAge<T> {
|
impl<T> ByMaxAge<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
|
let f = MAX_AGE_FILTERS;
|
||||||
|
let n = MAX_AGE_NAMES;
|
||||||
Self {
|
Self {
|
||||||
_1w: create(Filter::Time(TimeFilter::LowerThan(DAYS_1W))),
|
_1w: create(f._1w.clone(), n._1w.id),
|
||||||
_1m: create(Filter::Time(TimeFilter::LowerThan(DAYS_1M))),
|
_1m: create(f._1m.clone(), n._1m.id),
|
||||||
_2m: create(Filter::Time(TimeFilter::LowerThan(DAYS_2M))),
|
_2m: create(f._2m.clone(), n._2m.id),
|
||||||
_3m: create(Filter::Time(TimeFilter::LowerThan(DAYS_3M))),
|
_3m: create(f._3m.clone(), n._3m.id),
|
||||||
_4m: create(Filter::Time(TimeFilter::LowerThan(DAYS_4M))),
|
_4m: create(f._4m.clone(), n._4m.id),
|
||||||
_5m: create(Filter::Time(TimeFilter::LowerThan(DAYS_5M))),
|
_5m: create(f._5m.clone(), n._5m.id),
|
||||||
_6m: create(Filter::Time(TimeFilter::LowerThan(DAYS_6M))),
|
_6m: create(f._6m.clone(), n._6m.id),
|
||||||
_1y: create(Filter::Time(TimeFilter::LowerThan(DAYS_1Y))),
|
_1y: create(f._1y.clone(), n._1y.id),
|
||||||
_2y: create(Filter::Time(TimeFilter::LowerThan(DAYS_2Y))),
|
_2y: create(f._2y.clone(), n._2y.id),
|
||||||
_3y: create(Filter::Time(TimeFilter::LowerThan(DAYS_3Y))),
|
_3y: create(f._3y.clone(), n._3y.id),
|
||||||
_4y: create(Filter::Time(TimeFilter::LowerThan(DAYS_4Y))),
|
_4y: create(f._4y.clone(), n._4y.id),
|
||||||
_5y: create(Filter::Time(TimeFilter::LowerThan(DAYS_5Y))),
|
_5y: create(f._5y.clone(), n._5y.id),
|
||||||
_6y: create(Filter::Time(TimeFilter::LowerThan(DAYS_6Y))),
|
_6y: create(f._6y.clone(), n._6y.id),
|
||||||
_7y: create(Filter::Time(TimeFilter::LowerThan(DAYS_7Y))),
|
_7y: create(f._7y.clone(), n._7y.id),
|
||||||
_8y: create(Filter::Time(TimeFilter::LowerThan(DAYS_8Y))),
|
_8y: create(f._8y.clone(), n._8y.id),
|
||||||
_10y: create(Filter::Time(TimeFilter::LowerThan(DAYS_10Y))),
|
_10y: create(f._10y.clone(), n._10y.id),
|
||||||
_12y: create(Filter::Time(TimeFilter::LowerThan(DAYS_12Y))),
|
_12y: create(f._12y.clone(), n._12y.id),
|
||||||
_15y: create(Filter::Time(TimeFilter::LowerThan(DAYS_15Y))),
|
_15y: create(f._15y.clone(), n._15y.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
let f = MAX_AGE_FILTERS;
|
||||||
|
let n = MAX_AGE_NAMES;
|
||||||
|
Ok(Self {
|
||||||
|
_1w: create(f._1w.clone(), n._1w.id)?,
|
||||||
|
_1m: create(f._1m.clone(), n._1m.id)?,
|
||||||
|
_2m: create(f._2m.clone(), n._2m.id)?,
|
||||||
|
_3m: create(f._3m.clone(), n._3m.id)?,
|
||||||
|
_4m: create(f._4m.clone(), n._4m.id)?,
|
||||||
|
_5m: create(f._5m.clone(), n._5m.id)?,
|
||||||
|
_6m: create(f._6m.clone(), n._6m.id)?,
|
||||||
|
_1y: create(f._1y.clone(), n._1y.id)?,
|
||||||
|
_2y: create(f._2y.clone(), n._2y.id)?,
|
||||||
|
_3y: create(f._3y.clone(), n._3y.id)?,
|
||||||
|
_4y: create(f._4y.clone(), n._4y.id)?,
|
||||||
|
_5y: create(f._5y.clone(), n._5y.id)?,
|
||||||
|
_6y: create(f._6y.clone(), n._6y.id)?,
|
||||||
|
_7y: create(f._7y.clone(), n._7y.id)?,
|
||||||
|
_8y: create(f._8y.clone(), n._8y.id)?,
|
||||||
|
_10y: create(f._10y.clone(), n._10y.id)?,
|
||||||
|
_12y: create(f._12y.clone(), n._12y.id)?,
|
||||||
|
_15y: create(f._15y.clone(), n._15y.id)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
[
|
[
|
||||||
&self._1w, &self._1m, &self._2m, &self._3m, &self._4m, &self._5m, &self._6m, &self._1y,
|
&self._1w, &self._1m, &self._2m, &self._3m, &self._4m, &self._5m, &self._6m, &self._1y,
|
||||||
|
|||||||
@@ -1,12 +1,80 @@
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Filter, TimeFilter, DAYS_10Y, DAYS_12Y, DAYS_1D, DAYS_1M, DAYS_1W, DAYS_1Y, DAYS_2M, DAYS_2Y,
|
CohortName, Filter, TimeFilter, DAYS_10Y, DAYS_12Y, DAYS_1D, DAYS_1M, DAYS_1W, DAYS_1Y,
|
||||||
DAYS_3M, DAYS_3Y, DAYS_4M, DAYS_4Y, DAYS_5M, DAYS_5Y, DAYS_6M, DAYS_6Y, DAYS_7Y, DAYS_8Y,
|
DAYS_2M, DAYS_2Y, DAYS_3M, DAYS_3Y, DAYS_4M, DAYS_4Y, DAYS_5M, DAYS_5Y, DAYS_6M, DAYS_6Y,
|
||||||
|
DAYS_7Y, DAYS_8Y,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Clone, Traversable)]
|
/// Min age thresholds in days
|
||||||
|
pub const MIN_AGE_DAYS: ByMinAge<usize> = ByMinAge {
|
||||||
|
_1d: DAYS_1D,
|
||||||
|
_1w: DAYS_1W,
|
||||||
|
_1m: DAYS_1M,
|
||||||
|
_2m: DAYS_2M,
|
||||||
|
_3m: DAYS_3M,
|
||||||
|
_4m: DAYS_4M,
|
||||||
|
_5m: DAYS_5M,
|
||||||
|
_6m: DAYS_6M,
|
||||||
|
_1y: DAYS_1Y,
|
||||||
|
_2y: DAYS_2Y,
|
||||||
|
_3y: DAYS_3Y,
|
||||||
|
_4y: DAYS_4Y,
|
||||||
|
_5y: DAYS_5Y,
|
||||||
|
_6y: DAYS_6Y,
|
||||||
|
_7y: DAYS_7Y,
|
||||||
|
_8y: DAYS_8Y,
|
||||||
|
_10y: DAYS_10Y,
|
||||||
|
_12y: DAYS_12Y,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Min age filters (GreaterOrEqual threshold)
|
||||||
|
pub const MIN_AGE_FILTERS: ByMinAge<Filter> = ByMinAge {
|
||||||
|
_1d: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._1d)),
|
||||||
|
_1w: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._1w)),
|
||||||
|
_1m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._1m)),
|
||||||
|
_2m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._2m)),
|
||||||
|
_3m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._3m)),
|
||||||
|
_4m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._4m)),
|
||||||
|
_5m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._5m)),
|
||||||
|
_6m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._6m)),
|
||||||
|
_1y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._1y)),
|
||||||
|
_2y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._2y)),
|
||||||
|
_3y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._3y)),
|
||||||
|
_4y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._4y)),
|
||||||
|
_5y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._5y)),
|
||||||
|
_6y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._6y)),
|
||||||
|
_7y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._7y)),
|
||||||
|
_8y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._8y)),
|
||||||
|
_10y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._10y)),
|
||||||
|
_12y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._12y)),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Min age names
|
||||||
|
pub const MIN_AGE_NAMES: ByMinAge<CohortName> = ByMinAge {
|
||||||
|
_1d: CohortName::new("at_least_1d_old", "1d+", "At Least 1 Day Old"),
|
||||||
|
_1w: CohortName::new("at_least_1w_old", "1w+", "At Least 1 Week Old"),
|
||||||
|
_1m: CohortName::new("at_least_1m_old", "1m+", "At Least 1 Month Old"),
|
||||||
|
_2m: CohortName::new("at_least_2m_old", "2m+", "At Least 2 Months Old"),
|
||||||
|
_3m: CohortName::new("at_least_3m_old", "3m+", "At Least 3 Months Old"),
|
||||||
|
_4m: CohortName::new("at_least_4m_old", "4m+", "At Least 4 Months Old"),
|
||||||
|
_5m: CohortName::new("at_least_5m_old", "5m+", "At Least 5 Months Old"),
|
||||||
|
_6m: CohortName::new("at_least_6m_old", "6m+", "At Least 6 Months Old"),
|
||||||
|
_1y: CohortName::new("at_least_1y_old", "1y+", "At Least 1 Year Old"),
|
||||||
|
_2y: CohortName::new("at_least_2y_old", "2y+", "At Least 2 Years Old"),
|
||||||
|
_3y: CohortName::new("at_least_3y_old", "3y+", "At Least 3 Years Old"),
|
||||||
|
_4y: CohortName::new("at_least_4y_old", "4y+", "At Least 4 Years Old"),
|
||||||
|
_5y: CohortName::new("at_least_5y_old", "5y+", "At Least 5 Years Old"),
|
||||||
|
_6y: CohortName::new("at_least_6y_old", "6y+", "At Least 6 Years Old"),
|
||||||
|
_7y: CohortName::new("at_least_7y_old", "7y+", "At Least 7 Years Old"),
|
||||||
|
_8y: CohortName::new("at_least_8y_old", "8y+", "At Least 8 Years Old"),
|
||||||
|
_10y: CohortName::new("at_least_10y_old", "10y+", "At Least 10 Years Old"),
|
||||||
|
_12y: CohortName::new("at_least_12y_old", "12y+", "At Least 12 Years Old"),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Traversable, Serialize)]
|
||||||
pub struct ByMinAge<T> {
|
pub struct ByMinAge<T> {
|
||||||
pub _1d: T,
|
pub _1d: T,
|
||||||
pub _1w: T,
|
pub _1w: T,
|
||||||
@@ -28,33 +96,69 @@ pub struct ByMinAge<T> {
|
|||||||
pub _12y: T,
|
pub _12y: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ByMinAge<CohortName> {
|
||||||
|
pub const fn names() -> &'static Self {
|
||||||
|
&MIN_AGE_NAMES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ByMinAge<T> {
|
impl<T> ByMinAge<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
|
let f = MIN_AGE_FILTERS;
|
||||||
|
let n = MIN_AGE_NAMES;
|
||||||
Self {
|
Self {
|
||||||
_1d: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1D))),
|
_1d: create(f._1d.clone(), n._1d.id),
|
||||||
_1w: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1W))),
|
_1w: create(f._1w.clone(), n._1w.id),
|
||||||
_1m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1M))),
|
_1m: create(f._1m.clone(), n._1m.id),
|
||||||
_2m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2M))),
|
_2m: create(f._2m.clone(), n._2m.id),
|
||||||
_3m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3M))),
|
_3m: create(f._3m.clone(), n._3m.id),
|
||||||
_4m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4M))),
|
_4m: create(f._4m.clone(), n._4m.id),
|
||||||
_5m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5M))),
|
_5m: create(f._5m.clone(), n._5m.id),
|
||||||
_6m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6M))),
|
_6m: create(f._6m.clone(), n._6m.id),
|
||||||
_1y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1Y))),
|
_1y: create(f._1y.clone(), n._1y.id),
|
||||||
_2y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2Y))),
|
_2y: create(f._2y.clone(), n._2y.id),
|
||||||
_3y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3Y))),
|
_3y: create(f._3y.clone(), n._3y.id),
|
||||||
_4y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4Y))),
|
_4y: create(f._4y.clone(), n._4y.id),
|
||||||
_5y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5Y))),
|
_5y: create(f._5y.clone(), n._5y.id),
|
||||||
_6y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6Y))),
|
_6y: create(f._6y.clone(), n._6y.id),
|
||||||
_7y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_7Y))),
|
_7y: create(f._7y.clone(), n._7y.id),
|
||||||
_8y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_8Y))),
|
_8y: create(f._8y.clone(), n._8y.id),
|
||||||
_10y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_10Y))),
|
_10y: create(f._10y.clone(), n._10y.id),
|
||||||
_12y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_12Y))),
|
_12y: create(f._12y.clone(), n._12y.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
let f = MIN_AGE_FILTERS;
|
||||||
|
let n = MIN_AGE_NAMES;
|
||||||
|
Ok(Self {
|
||||||
|
_1d: create(f._1d.clone(), n._1d.id)?,
|
||||||
|
_1w: create(f._1w.clone(), n._1w.id)?,
|
||||||
|
_1m: create(f._1m.clone(), n._1m.id)?,
|
||||||
|
_2m: create(f._2m.clone(), n._2m.id)?,
|
||||||
|
_3m: create(f._3m.clone(), n._3m.id)?,
|
||||||
|
_4m: create(f._4m.clone(), n._4m.id)?,
|
||||||
|
_5m: create(f._5m.clone(), n._5m.id)?,
|
||||||
|
_6m: create(f._6m.clone(), n._6m.id)?,
|
||||||
|
_1y: create(f._1y.clone(), n._1y.id)?,
|
||||||
|
_2y: create(f._2y.clone(), n._2y.id)?,
|
||||||
|
_3y: create(f._3y.clone(), n._3y.id)?,
|
||||||
|
_4y: create(f._4y.clone(), n._4y.id)?,
|
||||||
|
_5y: create(f._5y.clone(), n._5y.id)?,
|
||||||
|
_6y: create(f._6y.clone(), n._6y.id)?,
|
||||||
|
_7y: create(f._7y.clone(), n._7y.id)?,
|
||||||
|
_8y: create(f._8y.clone(), n._8y.id)?,
|
||||||
|
_10y: create(f._10y.clone(), n._10y.id)?,
|
||||||
|
_12y: create(f._12y.clone(), n._12y.id)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
[
|
[
|
||||||
&self._1d, &self._1w, &self._1m, &self._2m, &self._3m, &self._4m, &self._5m, &self._6m,
|
&self._1d, &self._1w, &self._1m, &self._2m, &self._3m, &self._4m, &self._5m, &self._6m,
|
||||||
|
|||||||
@@ -3,10 +3,56 @@ use std::ops::{Add, AddAssign};
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::OutputType;
|
use brk_types::OutputType;
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::Filter;
|
use super::{CohortName, Filter};
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug, Traversable)]
|
/// Spendable type values
|
||||||
|
pub const SPENDABLE_TYPE_VALUES: BySpendableType<OutputType> = BySpendableType {
|
||||||
|
p2pk65: OutputType::P2PK65,
|
||||||
|
p2pk33: OutputType::P2PK33,
|
||||||
|
p2pkh: OutputType::P2PKH,
|
||||||
|
p2ms: OutputType::P2MS,
|
||||||
|
p2sh: OutputType::P2SH,
|
||||||
|
p2wpkh: OutputType::P2WPKH,
|
||||||
|
p2wsh: OutputType::P2WSH,
|
||||||
|
p2tr: OutputType::P2TR,
|
||||||
|
p2a: OutputType::P2A,
|
||||||
|
unknown: OutputType::Unknown,
|
||||||
|
empty: OutputType::Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Spendable type filters
|
||||||
|
pub const SPENDABLE_TYPE_FILTERS: BySpendableType<Filter> = BySpendableType {
|
||||||
|
p2pk65: Filter::Type(SPENDABLE_TYPE_VALUES.p2pk65),
|
||||||
|
p2pk33: Filter::Type(SPENDABLE_TYPE_VALUES.p2pk33),
|
||||||
|
p2pkh: Filter::Type(SPENDABLE_TYPE_VALUES.p2pkh),
|
||||||
|
p2ms: Filter::Type(SPENDABLE_TYPE_VALUES.p2ms),
|
||||||
|
p2sh: Filter::Type(SPENDABLE_TYPE_VALUES.p2sh),
|
||||||
|
p2wpkh: Filter::Type(SPENDABLE_TYPE_VALUES.p2wpkh),
|
||||||
|
p2wsh: Filter::Type(SPENDABLE_TYPE_VALUES.p2wsh),
|
||||||
|
p2tr: Filter::Type(SPENDABLE_TYPE_VALUES.p2tr),
|
||||||
|
p2a: Filter::Type(SPENDABLE_TYPE_VALUES.p2a),
|
||||||
|
unknown: Filter::Type(SPENDABLE_TYPE_VALUES.unknown),
|
||||||
|
empty: Filter::Type(SPENDABLE_TYPE_VALUES.empty),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Spendable type names
|
||||||
|
pub const SPENDABLE_TYPE_NAMES: BySpendableType<CohortName> = BySpendableType {
|
||||||
|
p2pk65: CohortName::new("p2pk65", "P2PK65", "Pay to Public Key (65 bytes)"),
|
||||||
|
p2pk33: CohortName::new("p2pk33", "P2PK33", "Pay to Public Key (33 bytes)"),
|
||||||
|
p2pkh: CohortName::new("p2pkh", "P2PKH", "Pay to Public Key Hash"),
|
||||||
|
p2ms: CohortName::new("p2ms", "P2MS", "Pay to Multisig"),
|
||||||
|
p2sh: CohortName::new("p2sh", "P2SH", "Pay to Script Hash"),
|
||||||
|
p2wpkh: CohortName::new("p2wpkh", "P2WPKH", "Pay to Witness Public Key Hash"),
|
||||||
|
p2wsh: CohortName::new("p2wsh", "P2WSH", "Pay to Witness Script Hash"),
|
||||||
|
p2tr: CohortName::new("p2tr", "P2TR", "Pay to Taproot"),
|
||||||
|
p2a: CohortName::new("p2a", "P2A", "Pay to Anchor"),
|
||||||
|
unknown: CohortName::new("unknown_outputs", "Unknown", "Unknown Output Type"),
|
||||||
|
empty: CohortName::new("empty_outputs", "Empty", "Empty Output"),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug, Traversable, Serialize)]
|
||||||
pub struct BySpendableType<T> {
|
pub struct BySpendableType<T> {
|
||||||
pub p2pk65: T,
|
pub p2pk65: T,
|
||||||
pub p2pk33: T,
|
pub p2pk33: T,
|
||||||
@@ -21,26 +67,55 @@ pub struct BySpendableType<T> {
|
|||||||
pub empty: T,
|
pub empty: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BySpendableType<CohortName> {
|
||||||
|
pub const fn names() -> &'static Self {
|
||||||
|
&SPENDABLE_TYPE_NAMES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> BySpendableType<T> {
|
impl<T> BySpendableType<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
|
let f = SPENDABLE_TYPE_FILTERS;
|
||||||
|
let n = SPENDABLE_TYPE_NAMES;
|
||||||
Self {
|
Self {
|
||||||
p2pk65: create(Filter::Type(OutputType::P2PK65)),
|
p2pk65: create(f.p2pk65, n.p2pk65.id),
|
||||||
p2pk33: create(Filter::Type(OutputType::P2PK33)),
|
p2pk33: create(f.p2pk33, n.p2pk33.id),
|
||||||
p2pkh: create(Filter::Type(OutputType::P2PKH)),
|
p2pkh: create(f.p2pkh, n.p2pkh.id),
|
||||||
p2ms: create(Filter::Type(OutputType::P2MS)),
|
p2ms: create(f.p2ms, n.p2ms.id),
|
||||||
p2sh: create(Filter::Type(OutputType::P2SH)),
|
p2sh: create(f.p2sh, n.p2sh.id),
|
||||||
p2wpkh: create(Filter::Type(OutputType::P2WPKH)),
|
p2wpkh: create(f.p2wpkh, n.p2wpkh.id),
|
||||||
p2wsh: create(Filter::Type(OutputType::P2WSH)),
|
p2wsh: create(f.p2wsh, n.p2wsh.id),
|
||||||
p2tr: create(Filter::Type(OutputType::P2TR)),
|
p2tr: create(f.p2tr, n.p2tr.id),
|
||||||
p2a: create(Filter::Type(OutputType::P2A)),
|
p2a: create(f.p2a, n.p2a.id),
|
||||||
unknown: create(Filter::Type(OutputType::Unknown)),
|
unknown: create(f.unknown, n.unknown.id),
|
||||||
empty: create(Filter::Type(OutputType::Empty)),
|
empty: create(f.empty, n.empty.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
let f = SPENDABLE_TYPE_FILTERS;
|
||||||
|
let n = SPENDABLE_TYPE_NAMES;
|
||||||
|
Ok(Self {
|
||||||
|
p2pk65: create(f.p2pk65, n.p2pk65.id)?,
|
||||||
|
p2pk33: create(f.p2pk33, n.p2pk33.id)?,
|
||||||
|
p2pkh: create(f.p2pkh, n.p2pkh.id)?,
|
||||||
|
p2ms: create(f.p2ms, n.p2ms.id)?,
|
||||||
|
p2sh: create(f.p2sh, n.p2sh.id)?,
|
||||||
|
p2wpkh: create(f.p2wpkh, n.p2wpkh.id)?,
|
||||||
|
p2wsh: create(f.p2wsh, n.p2wsh.id)?,
|
||||||
|
p2tr: create(f.p2tr, n.p2tr.id)?,
|
||||||
|
p2a: create(f.p2a, n.p2a.id)?,
|
||||||
|
unknown: create(f.unknown, n.unknown.id)?,
|
||||||
|
empty: create(f.empty, n.empty.id)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, output_type: OutputType) -> &mut T {
|
pub fn get_mut(&mut self, output_type: OutputType) -> &mut T {
|
||||||
match output_type {
|
match output_type {
|
||||||
OutputType::P2PK65 => &mut self.p2pk65,
|
OutputType::P2PK65 => &mut self.p2pk65,
|
||||||
|
|||||||
@@ -1,25 +1,64 @@
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::{Filter, Term};
|
use super::{CohortName, Filter, Term};
|
||||||
|
|
||||||
#[derive(Default, Clone, Traversable)]
|
/// Term values
|
||||||
|
pub const TERM_VALUES: ByTerm<Term> = ByTerm {
|
||||||
|
short: Term::Sth,
|
||||||
|
long: Term::Lth,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Term filters
|
||||||
|
pub const TERM_FILTERS: ByTerm<Filter> = ByTerm {
|
||||||
|
short: Filter::Term(TERM_VALUES.short),
|
||||||
|
long: Filter::Term(TERM_VALUES.long),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Term names
|
||||||
|
pub const TERM_NAMES: ByTerm<CohortName> = ByTerm {
|
||||||
|
short: CohortName::new("sth", "STH", "Short Term Holders"),
|
||||||
|
long: CohortName::new("lth", "LTH", "Long Term Holders"),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Traversable, Serialize)]
|
||||||
pub struct ByTerm<T> {
|
pub struct ByTerm<T> {
|
||||||
pub short: T,
|
pub short: T,
|
||||||
pub long: T,
|
pub long: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ByTerm<CohortName> {
|
||||||
|
pub const fn names() -> &'static Self {
|
||||||
|
&TERM_NAMES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ByTerm<T> {
|
impl<T> ByTerm<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
|
let f = TERM_FILTERS;
|
||||||
|
let n = TERM_NAMES;
|
||||||
Self {
|
Self {
|
||||||
short: create(Filter::Term(Term::Sth)),
|
short: create(f.short, n.short.id),
|
||||||
long: create(Filter::Term(Term::Lth)),
|
long: create(f.long, n.long.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
let f = TERM_FILTERS;
|
||||||
|
let n = TERM_NAMES;
|
||||||
|
Ok(Self {
|
||||||
|
short: create(f.short, n.short.id)?,
|
||||||
|
long: create(f.long, n.long.id)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
[&self.short, &self.long].into_iter()
|
[&self.short, &self.long].into_iter()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,77 @@
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{Timestamp, Year};
|
use brk_types::{Timestamp, Year};
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::Filter;
|
use super::{CohortName, Filter};
|
||||||
|
|
||||||
#[derive(Default, Clone, Traversable)]
|
/// Year values
|
||||||
|
pub const YEAR_VALUES: ByYear<Year> = ByYear {
|
||||||
|
_2009: Year::new(2009),
|
||||||
|
_2010: Year::new(2010),
|
||||||
|
_2011: Year::new(2011),
|
||||||
|
_2012: Year::new(2012),
|
||||||
|
_2013: Year::new(2013),
|
||||||
|
_2014: Year::new(2014),
|
||||||
|
_2015: Year::new(2015),
|
||||||
|
_2016: Year::new(2016),
|
||||||
|
_2017: Year::new(2017),
|
||||||
|
_2018: Year::new(2018),
|
||||||
|
_2019: Year::new(2019),
|
||||||
|
_2020: Year::new(2020),
|
||||||
|
_2021: Year::new(2021),
|
||||||
|
_2022: Year::new(2022),
|
||||||
|
_2023: Year::new(2023),
|
||||||
|
_2024: Year::new(2024),
|
||||||
|
_2025: Year::new(2025),
|
||||||
|
_2026: Year::new(2026),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Year filters
|
||||||
|
pub const YEAR_FILTERS: ByYear<Filter> = ByYear {
|
||||||
|
_2009: Filter::Year(YEAR_VALUES._2009),
|
||||||
|
_2010: Filter::Year(YEAR_VALUES._2010),
|
||||||
|
_2011: Filter::Year(YEAR_VALUES._2011),
|
||||||
|
_2012: Filter::Year(YEAR_VALUES._2012),
|
||||||
|
_2013: Filter::Year(YEAR_VALUES._2013),
|
||||||
|
_2014: Filter::Year(YEAR_VALUES._2014),
|
||||||
|
_2015: Filter::Year(YEAR_VALUES._2015),
|
||||||
|
_2016: Filter::Year(YEAR_VALUES._2016),
|
||||||
|
_2017: Filter::Year(YEAR_VALUES._2017),
|
||||||
|
_2018: Filter::Year(YEAR_VALUES._2018),
|
||||||
|
_2019: Filter::Year(YEAR_VALUES._2019),
|
||||||
|
_2020: Filter::Year(YEAR_VALUES._2020),
|
||||||
|
_2021: Filter::Year(YEAR_VALUES._2021),
|
||||||
|
_2022: Filter::Year(YEAR_VALUES._2022),
|
||||||
|
_2023: Filter::Year(YEAR_VALUES._2023),
|
||||||
|
_2024: Filter::Year(YEAR_VALUES._2024),
|
||||||
|
_2025: Filter::Year(YEAR_VALUES._2025),
|
||||||
|
_2026: Filter::Year(YEAR_VALUES._2026),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Year names
|
||||||
|
pub const YEAR_NAMES: ByYear<CohortName> = ByYear {
|
||||||
|
_2009: CohortName::new("year_2009", "2009", "Year 2009"),
|
||||||
|
_2010: CohortName::new("year_2010", "2010", "Year 2010"),
|
||||||
|
_2011: CohortName::new("year_2011", "2011", "Year 2011"),
|
||||||
|
_2012: CohortName::new("year_2012", "2012", "Year 2012"),
|
||||||
|
_2013: CohortName::new("year_2013", "2013", "Year 2013"),
|
||||||
|
_2014: CohortName::new("year_2014", "2014", "Year 2014"),
|
||||||
|
_2015: CohortName::new("year_2015", "2015", "Year 2015"),
|
||||||
|
_2016: CohortName::new("year_2016", "2016", "Year 2016"),
|
||||||
|
_2017: CohortName::new("year_2017", "2017", "Year 2017"),
|
||||||
|
_2018: CohortName::new("year_2018", "2018", "Year 2018"),
|
||||||
|
_2019: CohortName::new("year_2019", "2019", "Year 2019"),
|
||||||
|
_2020: CohortName::new("year_2020", "2020", "Year 2020"),
|
||||||
|
_2021: CohortName::new("year_2021", "2021", "Year 2021"),
|
||||||
|
_2022: CohortName::new("year_2022", "2022", "Year 2022"),
|
||||||
|
_2023: CohortName::new("year_2023", "2023", "Year 2023"),
|
||||||
|
_2024: CohortName::new("year_2024", "2024", "Year 2024"),
|
||||||
|
_2025: CohortName::new("year_2025", "2025", "Year 2025"),
|
||||||
|
_2026: CohortName::new("year_2026", "2026", "Year 2026"),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Traversable, Serialize)]
|
||||||
pub struct ByYear<T> {
|
pub struct ByYear<T> {
|
||||||
pub _2009: T,
|
pub _2009: T,
|
||||||
pub _2010: T,
|
pub _2010: T,
|
||||||
@@ -26,33 +93,69 @@ pub struct ByYear<T> {
|
|||||||
pub _2026: T,
|
pub _2026: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ByYear<CohortName> {
|
||||||
|
pub const fn names() -> &'static Self {
|
||||||
|
&YEAR_NAMES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ByYear<T> {
|
impl<T> ByYear<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
|
let f = YEAR_FILTERS;
|
||||||
|
let n = YEAR_NAMES;
|
||||||
Self {
|
Self {
|
||||||
_2009: create(Filter::Year(Year::new(2009))),
|
_2009: create(f._2009, n._2009.id),
|
||||||
_2010: create(Filter::Year(Year::new(2010))),
|
_2010: create(f._2010, n._2010.id),
|
||||||
_2011: create(Filter::Year(Year::new(2011))),
|
_2011: create(f._2011, n._2011.id),
|
||||||
_2012: create(Filter::Year(Year::new(2012))),
|
_2012: create(f._2012, n._2012.id),
|
||||||
_2013: create(Filter::Year(Year::new(2013))),
|
_2013: create(f._2013, n._2013.id),
|
||||||
_2014: create(Filter::Year(Year::new(2014))),
|
_2014: create(f._2014, n._2014.id),
|
||||||
_2015: create(Filter::Year(Year::new(2015))),
|
_2015: create(f._2015, n._2015.id),
|
||||||
_2016: create(Filter::Year(Year::new(2016))),
|
_2016: create(f._2016, n._2016.id),
|
||||||
_2017: create(Filter::Year(Year::new(2017))),
|
_2017: create(f._2017, n._2017.id),
|
||||||
_2018: create(Filter::Year(Year::new(2018))),
|
_2018: create(f._2018, n._2018.id),
|
||||||
_2019: create(Filter::Year(Year::new(2019))),
|
_2019: create(f._2019, n._2019.id),
|
||||||
_2020: create(Filter::Year(Year::new(2020))),
|
_2020: create(f._2020, n._2020.id),
|
||||||
_2021: create(Filter::Year(Year::new(2021))),
|
_2021: create(f._2021, n._2021.id),
|
||||||
_2022: create(Filter::Year(Year::new(2022))),
|
_2022: create(f._2022, n._2022.id),
|
||||||
_2023: create(Filter::Year(Year::new(2023))),
|
_2023: create(f._2023, n._2023.id),
|
||||||
_2024: create(Filter::Year(Year::new(2024))),
|
_2024: create(f._2024, n._2024.id),
|
||||||
_2025: create(Filter::Year(Year::new(2025))),
|
_2025: create(f._2025, n._2025.id),
|
||||||
_2026: create(Filter::Year(Year::new(2026))),
|
_2026: create(f._2026, n._2026.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||||
|
{
|
||||||
|
let f = YEAR_FILTERS;
|
||||||
|
let n = YEAR_NAMES;
|
||||||
|
Ok(Self {
|
||||||
|
_2009: create(f._2009, n._2009.id)?,
|
||||||
|
_2010: create(f._2010, n._2010.id)?,
|
||||||
|
_2011: create(f._2011, n._2011.id)?,
|
||||||
|
_2012: create(f._2012, n._2012.id)?,
|
||||||
|
_2013: create(f._2013, n._2013.id)?,
|
||||||
|
_2014: create(f._2014, n._2014.id)?,
|
||||||
|
_2015: create(f._2015, n._2015.id)?,
|
||||||
|
_2016: create(f._2016, n._2016.id)?,
|
||||||
|
_2017: create(f._2017, n._2017.id)?,
|
||||||
|
_2018: create(f._2018, n._2018.id)?,
|
||||||
|
_2019: create(f._2019, n._2019.id)?,
|
||||||
|
_2020: create(f._2020, n._2020.id)?,
|
||||||
|
_2021: create(f._2021, n._2021.id)?,
|
||||||
|
_2022: create(f._2022, n._2022.id)?,
|
||||||
|
_2023: create(f._2023, n._2023.id)?,
|
||||||
|
_2024: create(f._2024, n._2024.id)?,
|
||||||
|
_2025: create(f._2025, n._2025.id)?,
|
||||||
|
_2026: create(f._2026, n._2026.id)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
[
|
[
|
||||||
&self._2009,
|
&self._2009,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use super::Filter;
|
||||||
|
|
||||||
/// Context for cohort naming - determines whether a prefix is needed.
|
/// Context for cohort naming - determines whether a prefix is needed.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum CohortContext {
|
pub enum CohortContext {
|
||||||
@@ -14,4 +16,22 @@ impl CohortContext {
|
|||||||
CohortContext::Address => "addrs",
|
CohortContext::Address => "addrs",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prefixed(&self, name: &str) -> String {
|
||||||
|
format!("{}_{}", self.prefix(), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build full name for a filter, adding prefix only for Time/Amount filters.
|
||||||
|
///
|
||||||
|
/// Prefix rules:
|
||||||
|
/// - No prefix: `All`, `Term`, `Epoch`, `Year`, `Type`
|
||||||
|
/// - Context prefix: `Time`, `Amount`
|
||||||
|
pub fn full_name(&self, filter: &Filter, name: &str) -> String {
|
||||||
|
match filter {
|
||||||
|
Filter::All | Filter::Term(_) | Filter::Epoch(_) | Filter::Year(_) | Filter::Type(_) => {
|
||||||
|
name.to_string()
|
||||||
|
}
|
||||||
|
Filter::Time(_) | Filter::Amount(_) => self.prefixed(name),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
/// Display names for a cohort with id (for storage/API), short (for charts), and long (for tooltips/labels)
|
||||||
|
#[derive(Clone, Copy, Serialize)]
|
||||||
|
pub struct CohortName {
|
||||||
|
pub id: &'static str,
|
||||||
|
pub short: &'static str,
|
||||||
|
pub long: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CohortName {
|
||||||
|
pub const fn new(id: &'static str, short: &'static str, long: &'static str) -> Self {
|
||||||
|
Self { id, short, long }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,47 +29,6 @@ impl Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_name_suffix(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Filter::All => String::new(),
|
|
||||||
Filter::Term(t) => t.to_name().to_string(),
|
|
||||||
Filter::Time(t) => t.to_name_suffix(),
|
|
||||||
Filter::Amount(a) => a.to_name_suffix(),
|
|
||||||
Filter::Epoch(e) => format!("epoch_{}", usize::from(*e)),
|
|
||||||
Filter::Year(y) => format!("year_{}", u16::from(*y)),
|
|
||||||
Filter::Type(t) => match t {
|
|
||||||
OutputType::P2MS => "p2ms_outputs".to_string(),
|
|
||||||
OutputType::Empty => "empty_outputs".to_string(),
|
|
||||||
OutputType::Unknown => "unknown_outputs".to_string(),
|
|
||||||
_ => format!("{:?}", t).to_lowercase(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the full name for this filter, including context-based prefix.
|
|
||||||
///
|
|
||||||
/// Prefix rules:
|
|
||||||
/// - No prefix: `All`, `Term`, `Epoch`, `Type`
|
|
||||||
/// - `utxos_` prefix: `Time` or `Amount` with `CohortContext::Utxo`
|
|
||||||
/// - `addrs_` prefix: `Amount` with `CohortContext::Address`
|
|
||||||
pub fn to_full_name(&self, context: CohortContext) -> String {
|
|
||||||
let suffix = self.to_name_suffix();
|
|
||||||
if suffix.is_empty() {
|
|
||||||
return suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
let needs_prefix = match self {
|
|
||||||
Filter::All | Filter::Term(_) | Filter::Epoch(_) | Filter::Year(_) | Filter::Type(_) => false,
|
|
||||||
Filter::Time(_) | Filter::Amount(_) => true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if needs_prefix {
|
|
||||||
format!("{}_{}", context.prefix(), suffix)
|
|
||||||
} else {
|
|
||||||
suffix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if a time value (days) is contained by this filter
|
/// Check if a time value (days) is contained by this filter
|
||||||
pub fn contains_time(&self, days: usize) -> bool {
|
pub fn contains_time(&self, days: usize) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{CohortContext, Filter};
|
use super::Filter;
|
||||||
|
|
||||||
pub trait Filtered {
|
pub trait Filtered {
|
||||||
fn filter(&self) -> &Filter;
|
fn filter(&self) -> &Filter;
|
||||||
@@ -10,12 +10,4 @@ pub trait Filtered {
|
|||||||
fn includes_first_day(&self) -> bool {
|
fn includes_first_day(&self) -> bool {
|
||||||
self.filter().includes_first_day()
|
self.filter().includes_first_day()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name_suffix(&self) -> String {
|
|
||||||
self.filter().to_name_suffix()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn full_name(&self, context: CohortContext) -> String {
|
|
||||||
self.filter().to_full_name(context)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ mod by_term;
|
|||||||
mod by_type;
|
mod by_type;
|
||||||
mod by_unspendable_type;
|
mod by_unspendable_type;
|
||||||
mod cohort_context;
|
mod cohort_context;
|
||||||
|
mod cohort_name;
|
||||||
mod filter;
|
mod filter;
|
||||||
mod filtered;
|
mod filtered;
|
||||||
mod state_level;
|
mod state_level;
|
||||||
@@ -41,6 +42,7 @@ pub use by_term::*;
|
|||||||
pub use by_type::*;
|
pub use by_type::*;
|
||||||
pub use by_unspendable_type::*;
|
pub use by_unspendable_type::*;
|
||||||
pub use cohort_context::*;
|
pub use cohort_context::*;
|
||||||
|
pub use cohort_name::*;
|
||||||
pub use filter::*;
|
pub use filter::*;
|
||||||
pub use filtered::*;
|
pub use filtered::*;
|
||||||
pub use state_level::*;
|
pub use state_level::*;
|
||||||
|
|||||||
@@ -12,11 +12,4 @@ pub enum Term {
|
|||||||
|
|
||||||
impl Term {
|
impl Term {
|
||||||
pub const THRESHOLD_DAYS: usize = DAYS_5M;
|
pub const THRESHOLD_DAYS: usize = DAYS_5M;
|
||||||
|
|
||||||
pub fn to_name(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Term::Sth => "sth",
|
|
||||||
Term::Lth => "lth",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,75 +40,4 @@ impl TimeFilter {
|
|||||||
TimeFilter::GreaterOrEqual(_) => false,
|
TimeFilter::GreaterOrEqual(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_name_suffix(&self) -> String {
|
|
||||||
match self {
|
|
||||||
// Special cases for common filters
|
|
||||||
TimeFilter::LowerThan(1) => "up_to_1d_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(7) => "up_to_1w_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(30) => "up_to_1m_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(60) => "up_to_2m_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(90) => "up_to_3m_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(120) => "up_to_4m_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(150) => "sth".to_string(),
|
|
||||||
TimeFilter::LowerThan(180) => "up_to_6m_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(365) => "up_to_1y_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(730) => "up_to_2y_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(1095) => "up_to_3y_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(1460) => "up_to_4y_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(1825) => "up_to_5y_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(2190) => "up_to_6y_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(2555) => "up_to_7y_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(2920) => "up_to_8y_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(3650) => "up_to_10y_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(4380) => "up_to_12y_old".to_string(),
|
|
||||||
TimeFilter::LowerThan(5475) => "up_to_15y_old".to_string(),
|
|
||||||
|
|
||||||
TimeFilter::GreaterOrEqual(1) => "at_least_1d_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(7) => "at_least_1w_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(30) => "at_least_1m_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(60) => "at_least_2m_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(90) => "at_least_3m_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(120) => "at_least_4m_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(150) => "lth".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(180) => "at_least_6m_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(365) => "at_least_1y_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(730) => "at_least_2y_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(1095) => "at_least_3y_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(1460) => "at_least_4y_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(1825) => "at_least_5y_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(2190) => "at_least_6y_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(2555) => "at_least_7y_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(2920) => "at_least_8y_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(3650) => "at_least_10y_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(4380) => "at_least_12y_old".to_string(),
|
|
||||||
TimeFilter::GreaterOrEqual(5475) => "at_least_15y_old".to_string(),
|
|
||||||
|
|
||||||
// Range special cases
|
|
||||||
TimeFilter::Range(r) if *r == (0..1) => "up_to_1d".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (1..7) => "at_least_1d_up_to_1w_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (7..30) => "at_least_1w_up_to_1m_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (30..60) => "at_least_1m_up_to_2m_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (60..90) => "at_least_2m_up_to_3m_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (90..120) => "at_least_3m_up_to_4m_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (120..150) => "at_least_4m_up_to_5m_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (150..180) => "at_least_5m_up_to_6m_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (180..365) => "at_least_6m_up_to_1y_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (365..730) => "at_least_1y_up_to_2y_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (730..1095) => "at_least_2y_up_to_3y_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (1095..1460) => "at_least_3y_up_to_4y_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (1460..1825) => "at_least_4y_up_to_5y_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (1825..2190) => "at_least_5y_up_to_6y_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (2190..2555) => "at_least_6y_up_to_7y_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (2555..2920) => "at_least_7y_up_to_8y_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (2920..3650) => "at_least_8y_up_to_10y_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (3650..4380) => "at_least_10y_up_to_12y_old".to_string(),
|
|
||||||
TimeFilter::Range(r) if *r == (4380..5475) => "at_least_12y_up_to_15y_old".to_string(),
|
|
||||||
|
|
||||||
// Fallback generic names
|
|
||||||
TimeFilter::LowerThan(d) => format!("up_to_{}d", d),
|
|
||||||
TimeFilter::GreaterOrEqual(d) => format!("at_least_{}d", d),
|
|
||||||
TimeFilter::Range(r) => format!("{}d_to_{}d", r.start, r.end),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ pub struct UTXOGroups<T> {
|
|||||||
impl<T> UTXOGroups<T> {
|
impl<T> UTXOGroups<T> {
|
||||||
pub fn new<F>(mut create: F) -> Self
|
pub fn new<F>(mut create: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Filter) -> T,
|
F: FnMut(Filter, &'static str) -> T,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
all: create(Filter::All),
|
all: create(Filter::All, ""),
|
||||||
age_range: ByAgeRange::new(&mut create),
|
age_range: ByAgeRange::new(&mut create),
|
||||||
epoch: ByEpoch::new(&mut create),
|
epoch: ByEpoch::new(&mut create),
|
||||||
year: ByYear::new(&mut create),
|
year: ByYear::new(&mut create),
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ pub const BLOCKS_PER_HALVING: u32 = 210_000;
|
|||||||
pub struct HalvingEpoch(u16);
|
pub struct HalvingEpoch(u16);
|
||||||
|
|
||||||
impl HalvingEpoch {
|
impl HalvingEpoch {
|
||||||
pub fn new(value: u16) -> Self {
|
pub const fn new(value: u16) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub struct Year(u16);
|
|||||||
impl Year {
|
impl Year {
|
||||||
pub const GENESIS: Self = Self(2009);
|
pub const GENESIS: Self = Self(2009);
|
||||||
|
|
||||||
pub fn new(value: u16) -> Self {
|
pub const fn new(value: u16) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1345
-404
File diff suppressed because it is too large
Load Diff
+948
-245
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user