mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
computer: stateful snapshot
This commit is contained in:
@@ -9,11 +9,11 @@ use std::path::Path;
|
||||
use brk_error::Result;
|
||||
use brk_grouper::{
|
||||
AmountFilter, ByAgeRange, ByAmountRange, ByEpoch, ByGreatEqualAmount, ByLowerThanAmount,
|
||||
ByMaxAge, ByMinAge, BySpendableType, ByTerm, Filter, Filtered, StateLevel, Term, TimeFilter,
|
||||
UTXOGroups,
|
||||
ByMaxAge, ByMinAge, BySpendableType, ByTerm, ByYear, Filter, Filtered, StateLevel, Term,
|
||||
TimeFilter, UTXOGroups,
|
||||
};
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, DateIndex, Dollars, HalvingEpoch, Height, OutputType, Sats, Version};
|
||||
use brk_types::{Bitcoin, DateIndex, Dollars, HalvingEpoch, Height, OutputType, Sats, Version, Year};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{Database, Exit, IterableVec};
|
||||
@@ -75,6 +75,27 @@ impl UTXOCohorts {
|
||||
_4: full(Filter::Epoch(HalvingEpoch::new(4)))?,
|
||||
},
|
||||
|
||||
year: ByYear {
|
||||
_2009: full(Filter::Year(Year::new(2009)))?,
|
||||
_2010: full(Filter::Year(Year::new(2010)))?,
|
||||
_2011: full(Filter::Year(Year::new(2011)))?,
|
||||
_2012: full(Filter::Year(Year::new(2012)))?,
|
||||
_2013: full(Filter::Year(Year::new(2013)))?,
|
||||
_2014: full(Filter::Year(Year::new(2014)))?,
|
||||
_2015: full(Filter::Year(Year::new(2015)))?,
|
||||
_2016: full(Filter::Year(Year::new(2016)))?,
|
||||
_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)))?,
|
||||
},
|
||||
|
||||
type_: BySpendableType {
|
||||
p2pk65: full(Filter::Type(OutputType::P2PK65))?,
|
||||
p2pk33: full(Filter::Type(OutputType::P2PK33))?,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Processing received outputs (new UTXOs).
|
||||
|
||||
use brk_grouper::{Filter, Filtered};
|
||||
use brk_types::{Dollars, Height};
|
||||
use brk_types::{Dollars, Height, Timestamp};
|
||||
|
||||
use crate::stateful::states::Transacted;
|
||||
|
||||
@@ -13,15 +13,23 @@ impl UTXOCohorts {
|
||||
/// New UTXOs are added to:
|
||||
/// - The "up_to_1d" age cohort (all new UTXOs start at 0 days old)
|
||||
/// - The appropriate epoch cohort based on block height
|
||||
/// - The appropriate year cohort based on block timestamp
|
||||
/// - The appropriate output type cohort (P2PKH, P2SH, etc.)
|
||||
/// - The appropriate amount range cohort based on value
|
||||
pub fn receive(&mut self, received: Transacted, height: Height, price: Option<Dollars>) {
|
||||
pub fn receive(
|
||||
&mut self,
|
||||
received: Transacted,
|
||||
height: Height,
|
||||
timestamp: Timestamp,
|
||||
price: Option<Dollars>,
|
||||
) {
|
||||
let supply_state = received.spendable_supply;
|
||||
|
||||
// New UTXOs go into up_to_1d and current epoch
|
||||
// New UTXOs go into up_to_1d, current epoch, and current year
|
||||
[
|
||||
&mut self.0.age_range.up_to_1d,
|
||||
self.0.epoch.mut_vec_from_height(height),
|
||||
self.0.year.mut_vec_from_timestamp(timestamp),
|
||||
]
|
||||
.into_iter()
|
||||
.for_each(|v| {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Processing spent inputs (UTXOs being spent).
|
||||
|
||||
use brk_grouper::{Filter, Filtered, TimeFilter};
|
||||
use brk_types::{CheckedSub, HalvingEpoch, Height};
|
||||
use brk_types::{CheckedSub, HalvingEpoch, Height, Year};
|
||||
use rustc_hash::FxHashMap;
|
||||
use vecdb::VecIndex;
|
||||
|
||||
@@ -26,12 +26,13 @@ impl UTXOCohorts {
|
||||
return;
|
||||
}
|
||||
|
||||
// Time-based cohorts: age_range + epoch
|
||||
// Time-based cohorts: age_range + epoch + year
|
||||
let mut time_cohorts: Vec<_> = self
|
||||
.0
|
||||
.age_range
|
||||
.iter_mut()
|
||||
.chain(self.0.epoch.iter_mut())
|
||||
.chain(self.0.year.iter_mut())
|
||||
.collect();
|
||||
|
||||
let last_block = chain_state.last().unwrap();
|
||||
@@ -62,6 +63,7 @@ impl UTXOCohorts {
|
||||
Filter::Time(TimeFilter::LowerThan(to)) => *to > days_old,
|
||||
Filter::Time(TimeFilter::Range(range)) => range.contains(&days_old),
|
||||
Filter::Epoch(e) => *e == HalvingEpoch::from(height),
|
||||
Filter::Year(y) => *y == Year::from(block_state.timestamp),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.for_each(|vecs| {
|
||||
|
||||
@@ -420,7 +420,7 @@ pub fn process_blocks(
|
||||
});
|
||||
|
||||
// Main thread: Update UTXO cohorts
|
||||
vecs.utxo_cohorts.receive(transacted, height, block_price);
|
||||
vecs.utxo_cohorts.receive(transacted, height, timestamp, block_price);
|
||||
vecs.utxo_cohorts.send(height_to_sent, chain_state);
|
||||
});
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ impl ActivityMetrics {
|
||||
pub fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
let compute_dollars = cfg.compute_dollars();
|
||||
let sum = VecBuilderOptions::default().add_sum();
|
||||
let sum_cum = VecBuilderOptions::default().add_sum().add_cumulative();
|
||||
|
||||
Ok(Self {
|
||||
height_to_sent: EagerVec::forced_import(cfg.db, &cfg.name("sent"), cfg.version + v0)?,
|
||||
@@ -52,7 +52,7 @@ impl ActivityMetrics {
|
||||
&cfg.name("sent"),
|
||||
Source::None,
|
||||
cfg.version + v0,
|
||||
sum,
|
||||
sum_cum,
|
||||
compute_dollars,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
@@ -75,7 +75,7 @@ impl ActivityMetrics {
|
||||
Source::Compute,
|
||||
cfg.version + v0,
|
||||
cfg.indexes,
|
||||
sum,
|
||||
sum_cum,
|
||||
)?,
|
||||
|
||||
indexes_to_coindays_destroyed: ComputedVecsFromHeight::forced_import(
|
||||
@@ -84,7 +84,7 @@ impl ActivityMetrics {
|
||||
Source::Compute,
|
||||
cfg.version + v0,
|
||||
cfg.indexes,
|
||||
sum,
|
||||
sum_cum,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
159
crates/brk_grouper/src/by_year.rs
Normal file
159
crates/brk_grouper/src/by_year.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Timestamp, Year};
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
|
||||
use super::Filter;
|
||||
|
||||
#[derive(Default, Clone, Traversable)]
|
||||
pub struct ByYear<T> {
|
||||
pub _2009: T,
|
||||
pub _2010: T,
|
||||
pub _2011: T,
|
||||
pub _2012: T,
|
||||
pub _2013: T,
|
||||
pub _2014: T,
|
||||
pub _2015: T,
|
||||
pub _2016: T,
|
||||
pub _2017: T,
|
||||
pub _2018: T,
|
||||
pub _2019: T,
|
||||
pub _2020: T,
|
||||
pub _2021: T,
|
||||
pub _2022: T,
|
||||
pub _2023: T,
|
||||
pub _2024: T,
|
||||
pub _2025: T,
|
||||
pub _2026: T,
|
||||
}
|
||||
|
||||
impl<T> ByYear<T> {
|
||||
pub fn new<F>(mut create: F) -> Self
|
||||
where
|
||||
F: FnMut(Filter) -> T,
|
||||
{
|
||||
Self {
|
||||
_2009: create(Filter::Year(Year::new(2009))),
|
||||
_2010: create(Filter::Year(Year::new(2010))),
|
||||
_2011: create(Filter::Year(Year::new(2011))),
|
||||
_2012: create(Filter::Year(Year::new(2012))),
|
||||
_2013: create(Filter::Year(Year::new(2013))),
|
||||
_2014: create(Filter::Year(Year::new(2014))),
|
||||
_2015: create(Filter::Year(Year::new(2015))),
|
||||
_2016: create(Filter::Year(Year::new(2016))),
|
||||
_2017: create(Filter::Year(Year::new(2017))),
|
||||
_2018: create(Filter::Year(Year::new(2018))),
|
||||
_2019: create(Filter::Year(Year::new(2019))),
|
||||
_2020: create(Filter::Year(Year::new(2020))),
|
||||
_2021: create(Filter::Year(Year::new(2021))),
|
||||
_2022: create(Filter::Year(Year::new(2022))),
|
||||
_2023: create(Filter::Year(Year::new(2023))),
|
||||
_2024: create(Filter::Year(Year::new(2024))),
|
||||
_2025: create(Filter::Year(Year::new(2025))),
|
||||
_2026: create(Filter::Year(Year::new(2026))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||
[
|
||||
&self._2009,
|
||||
&self._2010,
|
||||
&self._2011,
|
||||
&self._2012,
|
||||
&self._2013,
|
||||
&self._2014,
|
||||
&self._2015,
|
||||
&self._2016,
|
||||
&self._2017,
|
||||
&self._2018,
|
||||
&self._2019,
|
||||
&self._2020,
|
||||
&self._2021,
|
||||
&self._2022,
|
||||
&self._2023,
|
||||
&self._2024,
|
||||
&self._2025,
|
||||
&self._2026,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||
[
|
||||
&mut self._2009,
|
||||
&mut self._2010,
|
||||
&mut self._2011,
|
||||
&mut self._2012,
|
||||
&mut self._2013,
|
||||
&mut self._2014,
|
||||
&mut self._2015,
|
||||
&mut self._2016,
|
||||
&mut self._2017,
|
||||
&mut self._2018,
|
||||
&mut self._2019,
|
||||
&mut self._2020,
|
||||
&mut self._2021,
|
||||
&mut self._2022,
|
||||
&mut self._2023,
|
||||
&mut self._2024,
|
||||
&mut self._2025,
|
||||
&mut self._2026,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
|
||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut T>
|
||||
where
|
||||
T: Send + Sync,
|
||||
{
|
||||
[
|
||||
&mut self._2009,
|
||||
&mut self._2010,
|
||||
&mut self._2011,
|
||||
&mut self._2012,
|
||||
&mut self._2013,
|
||||
&mut self._2014,
|
||||
&mut self._2015,
|
||||
&mut self._2016,
|
||||
&mut self._2017,
|
||||
&mut self._2018,
|
||||
&mut self._2019,
|
||||
&mut self._2020,
|
||||
&mut self._2021,
|
||||
&mut self._2022,
|
||||
&mut self._2023,
|
||||
&mut self._2024,
|
||||
&mut self._2025,
|
||||
&mut self._2026,
|
||||
]
|
||||
.into_par_iter()
|
||||
}
|
||||
|
||||
pub fn mut_vec_from_timestamp(&mut self, timestamp: Timestamp) -> &mut T {
|
||||
let year = Year::from(timestamp);
|
||||
self.get_mut(year)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, year: Year) -> &mut T {
|
||||
match u16::from(year) {
|
||||
2009 => &mut self._2009,
|
||||
2010 => &mut self._2010,
|
||||
2011 => &mut self._2011,
|
||||
2012 => &mut self._2012,
|
||||
2013 => &mut self._2013,
|
||||
2014 => &mut self._2014,
|
||||
2015 => &mut self._2015,
|
||||
2016 => &mut self._2016,
|
||||
2017 => &mut self._2017,
|
||||
2018 => &mut self._2018,
|
||||
2019 => &mut self._2019,
|
||||
2020 => &mut self._2020,
|
||||
2021 => &mut self._2021,
|
||||
2022 => &mut self._2022,
|
||||
2023 => &mut self._2023,
|
||||
2024 => &mut self._2024,
|
||||
2025 => &mut self._2025,
|
||||
2026 => &mut self._2026,
|
||||
_ => todo!("Year {} not yet supported", u16::from(year)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use brk_types::{HalvingEpoch, OutputType, Sats};
|
||||
use brk_types::{HalvingEpoch, OutputType, Sats, Year};
|
||||
|
||||
use super::{AmountFilter, CohortContext, Term, TimeFilter};
|
||||
|
||||
@@ -9,6 +9,7 @@ pub enum Filter {
|
||||
Time(TimeFilter),
|
||||
Amount(AmountFilter),
|
||||
Epoch(HalvingEpoch),
|
||||
Year(Year),
|
||||
Type(OutputType),
|
||||
}
|
||||
|
||||
@@ -35,6 +36,7 @@ impl Filter {
|
||||
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(),
|
||||
@@ -57,7 +59,7 @@ impl Filter {
|
||||
}
|
||||
|
||||
let needs_prefix = match self {
|
||||
Filter::All | Filter::Term(_) | Filter::Epoch(_) | Filter::Type(_) => false,
|
||||
Filter::All | Filter::Term(_) | Filter::Epoch(_) | Filter::Year(_) | Filter::Type(_) => false,
|
||||
Filter::Time(_) | Filter::Amount(_) => true,
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ mod by_amount_range;
|
||||
mod by_any_address;
|
||||
mod by_epoch;
|
||||
mod by_ge_amount;
|
||||
mod by_year;
|
||||
mod by_lt_amount;
|
||||
mod by_max_age;
|
||||
mod by_min_age;
|
||||
@@ -31,6 +32,7 @@ pub use by_amount_range::*;
|
||||
pub use by_any_address::*;
|
||||
pub use by_epoch::*;
|
||||
pub use by_ge_amount::*;
|
||||
pub use by_year::*;
|
||||
pub use by_lt_amount::*;
|
||||
pub use by_max_age::*;
|
||||
pub use by_min_age::*;
|
||||
|
||||
@@ -3,7 +3,7 @@ use rayon::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ByAgeRange, ByAmountRange, ByEpoch, ByGreatEqualAmount, ByLowerThanAmount, ByMaxAge, ByMinAge,
|
||||
BySpendableType, ByTerm, Filter,
|
||||
BySpendableType, ByTerm, ByYear, Filter,
|
||||
};
|
||||
|
||||
#[derive(Default, Clone, Traversable)]
|
||||
@@ -11,6 +11,7 @@ pub struct UTXOGroups<T> {
|
||||
pub all: T,
|
||||
pub age_range: ByAgeRange<T>,
|
||||
pub epoch: ByEpoch<T>,
|
||||
pub year: ByYear<T>,
|
||||
pub min_age: ByMinAge<T>,
|
||||
pub ge_amount: ByGreatEqualAmount<T>,
|
||||
pub amount_range: ByAmountRange<T>,
|
||||
@@ -29,6 +30,7 @@ impl<T> UTXOGroups<T> {
|
||||
all: create(Filter::All),
|
||||
age_range: ByAgeRange::new(&mut create),
|
||||
epoch: ByEpoch::new(&mut create),
|
||||
year: ByYear::new(&mut create),
|
||||
min_age: ByMinAge::new(&mut create),
|
||||
ge_amount: ByGreatEqualAmount::new(&mut create),
|
||||
amount_range: ByAmountRange::new(&mut create),
|
||||
@@ -48,6 +50,7 @@ impl<T> UTXOGroups<T> {
|
||||
.chain(self.ge_amount.iter())
|
||||
.chain(self.age_range.iter())
|
||||
.chain(self.epoch.iter())
|
||||
.chain(self.year.iter())
|
||||
.chain(self.amount_range.iter())
|
||||
.chain(self.lt_amount.iter())
|
||||
.chain(self.type_.iter())
|
||||
@@ -62,6 +65,7 @@ impl<T> UTXOGroups<T> {
|
||||
.chain(self.ge_amount.iter_mut())
|
||||
.chain(self.age_range.iter_mut())
|
||||
.chain(self.epoch.iter_mut())
|
||||
.chain(self.year.iter_mut())
|
||||
.chain(self.amount_range.iter_mut())
|
||||
.chain(self.lt_amount.iter_mut())
|
||||
.chain(self.type_.iter_mut())
|
||||
@@ -79,6 +83,7 @@ impl<T> UTXOGroups<T> {
|
||||
.chain(self.ge_amount.par_iter_mut())
|
||||
.chain(self.age_range.par_iter_mut())
|
||||
.chain(self.epoch.par_iter_mut())
|
||||
.chain(self.year.par_iter_mut())
|
||||
.chain(self.amount_range.par_iter_mut())
|
||||
.chain(self.lt_amount.par_iter_mut())
|
||||
.chain(self.type_.par_iter_mut())
|
||||
@@ -88,6 +93,7 @@ impl<T> UTXOGroups<T> {
|
||||
self.age_range
|
||||
.iter()
|
||||
.chain(self.epoch.iter())
|
||||
.chain(self.year.iter())
|
||||
.chain(self.amount_range.iter())
|
||||
.chain(self.type_.iter())
|
||||
}
|
||||
@@ -96,6 +102,7 @@ impl<T> UTXOGroups<T> {
|
||||
self.age_range
|
||||
.iter_mut()
|
||||
.chain(self.epoch.iter_mut())
|
||||
.chain(self.year.iter_mut())
|
||||
.chain(self.amount_range.iter_mut())
|
||||
.chain(self.type_.iter_mut())
|
||||
}
|
||||
@@ -107,6 +114,7 @@ impl<T> UTXOGroups<T> {
|
||||
self.age_range
|
||||
.par_iter_mut()
|
||||
.chain(self.epoch.par_iter_mut())
|
||||
.chain(self.year.par_iter_mut())
|
||||
.chain(self.amount_range.par_iter_mut())
|
||||
.chain(self.type_.par_iter_mut())
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ mod vout;
|
||||
mod vsize;
|
||||
mod weekindex;
|
||||
mod weight;
|
||||
mod year;
|
||||
mod yearindex;
|
||||
|
||||
pub use address::*;
|
||||
@@ -304,4 +305,5 @@ pub use vout::*;
|
||||
pub use vsize::*;
|
||||
pub use weekindex::*;
|
||||
pub use weight::*;
|
||||
pub use year::*;
|
||||
pub use yearindex::*;
|
||||
|
||||
130
crates/brk_types/src/year.rs
Normal file
130
crates/brk_types/src/year.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
ops::{Add, AddAssign, Div},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
|
||||
|
||||
use super::{Date, Timestamp};
|
||||
|
||||
/// Bitcoin year (2009, 2010, ..., 2025+)
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash, Serialize, Deserialize, Pco,
|
||||
)]
|
||||
pub struct Year(u16);
|
||||
|
||||
impl Year {
|
||||
pub const GENESIS: Self = Self(2009);
|
||||
|
||||
pub fn new(value: u16) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
/// Returns the year as an index (0 = 2009, 1 = 2010, etc.)
|
||||
pub fn to_index(self) -> usize {
|
||||
(self.0 - 2009) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for Year {
|
||||
#[inline]
|
||||
fn from(value: u16) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Year {
|
||||
#[inline]
|
||||
fn from(value: usize) -> Self {
|
||||
Self(value as u16)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Year> for usize {
|
||||
#[inline]
|
||||
fn from(value: Year) -> Self {
|
||||
value.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Year> for u16 {
|
||||
#[inline]
|
||||
fn from(value: Year) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Year {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self::from(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Year {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self = *self + rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for Year {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
Self::from(self.0 + rhs as u16)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Timestamp> for Year {
|
||||
#[inline]
|
||||
fn from(value: Timestamp) -> Self {
|
||||
Self(Date::from(value).year())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Date> for Year {
|
||||
#[inline]
|
||||
fn from(value: Date) -> Self {
|
||||
Self(value.year())
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckedSub for Year {
|
||||
fn checked_sub(self, rhs: Self) -> Option<Self> {
|
||||
self.0.checked_sub(rhs.0).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<usize> for Year {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: usize) -> Self::Output {
|
||||
Self::from(self.0 as usize / rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableIndex for Year {
|
||||
fn to_string() -> &'static str {
|
||||
"year"
|
||||
}
|
||||
|
||||
fn to_possible_strings() -> &'static [&'static str] {
|
||||
&["year"]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Year {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut buf = itoa::Buffer::new();
|
||||
let str = buf.format(self.0);
|
||||
f.write_str(str)
|
||||
}
|
||||
}
|
||||
|
||||
impl Formattable for Year {
|
||||
#[inline(always)]
|
||||
fn may_need_escaping() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user