mithril_client/cardano_database_client/
immutable_file_range.rs

1use std::ops::RangeInclusive;
2
3use anyhow::anyhow;
4
5use mithril_common::{entities::ImmutableFileNumber, StdResult};
6
7/// Immutable file range representation
8#[derive(Debug, Eq, PartialEq)]
9pub enum ImmutableFileRange {
10    /// From the first (included) to the last immutable file number (included)
11    Full,
12
13    /// From a specific immutable file number (included) to the last immutable file number (included)
14    From(ImmutableFileNumber),
15
16    /// From a specific immutable file number (included) to another specific immutable file number (included)
17    Range(ImmutableFileNumber, ImmutableFileNumber),
18
19    /// From the first immutable file number (included) up to a specific immutable file number (included)
20    UpTo(ImmutableFileNumber),
21}
22
23impl ImmutableFileRange {
24    /// Returns the range of immutable file numbers
25    pub fn to_range_inclusive(
26        &self,
27        last_immutable_file_number: ImmutableFileNumber,
28    ) -> StdResult<RangeInclusive<ImmutableFileNumber>> {
29        // The immutable file numbers start from 1 on all the networks except the 'devnet'
30        // when it is configured with aggressive protocol parameters for fast epochs (used in the e2e tests).
31        // We have taken the choice to consider that the file numbers start from 1 for all the networks.
32        const FIRST_IMMUTABLE_FILE_NUMBER: ImmutableFileNumber = 1;
33        let full_range = FIRST_IMMUTABLE_FILE_NUMBER..=last_immutable_file_number;
34
35        match self {
36            ImmutableFileRange::Full => Ok(full_range),
37            ImmutableFileRange::From(from) if full_range.contains(from) => {
38                Ok(*from..=last_immutable_file_number)
39            }
40            ImmutableFileRange::Range(from, to)
41                if full_range.contains(from)
42                    && full_range.contains(to)
43                    && !(*from..=*to).is_empty() =>
44            {
45                Ok(*from..=*to)
46            }
47            ImmutableFileRange::UpTo(to) if full_range.contains(to) => {
48                Ok(FIRST_IMMUTABLE_FILE_NUMBER..=*to)
49            }
50            _ => Err(anyhow!("Invalid immutable file range: {self:?}")),
51        }
52    }
53
54    /// Returns the length of the immutable file range
55    pub fn length(&self, last_immutable_file_number: ImmutableFileNumber) -> u64 {
56        match self {
57            ImmutableFileRange::Full => last_immutable_file_number,
58            ImmutableFileRange::From(from) => last_immutable_file_number - from + 1,
59            ImmutableFileRange::Range(from, to) => to - from + 1,
60            ImmutableFileRange::UpTo(to) => *to,
61        }
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn to_range_inclusive_with_full() {
71        let immutable_file_range = ImmutableFileRange::Full;
72        let last_immutable_file_number = 10;
73
74        let result = immutable_file_range
75            .to_range_inclusive(last_immutable_file_number)
76            .unwrap();
77        assert_eq!(1..=10, result);
78    }
79
80    #[test]
81    fn to_range_inclusive_with_from() {
82        let immutable_file_range = ImmutableFileRange::From(5);
83
84        let last_immutable_file_number = 10;
85        let result = immutable_file_range
86            .to_range_inclusive(last_immutable_file_number)
87            .unwrap();
88        assert_eq!(5..=10, result);
89
90        let last_immutable_file_number = 3;
91        immutable_file_range
92            .to_range_inclusive(last_immutable_file_number)
93            .expect_err("should fail: given last immutable should be greater than range start");
94    }
95
96    #[test]
97    fn to_range_inclusive_with_range() {
98        let immutable_file_range = ImmutableFileRange::Range(5, 8);
99
100        let last_immutable_file_number = 10;
101        let result = immutable_file_range
102            .to_range_inclusive(last_immutable_file_number)
103            .unwrap();
104        assert_eq!(5..=8, result);
105
106        let last_immutable_file_number = 7;
107        immutable_file_range
108            .to_range_inclusive(last_immutable_file_number)
109            .expect_err(
110                "should fail: given last immutable should be greater or equal range max bound",
111            );
112
113        let immutable_file_range = ImmutableFileRange::Range(10, 8);
114        immutable_file_range
115            .to_range_inclusive(last_immutable_file_number)
116            .expect_err("should fail: range start should be lower than range end");
117    }
118
119    #[test]
120    fn to_range_inclusive_with_up_to() {
121        let immutable_file_range = ImmutableFileRange::UpTo(8);
122
123        let last_immutable_file_number = 10;
124        let result = immutable_file_range
125            .to_range_inclusive(last_immutable_file_number)
126            .unwrap();
127        assert_eq!(1..=8, result);
128
129        let last_immutable_file_number = 7;
130        immutable_file_range
131            .to_range_inclusive(last_immutable_file_number)
132            .expect_err(
133                "should fail: given last immutable should be greater or equal range max bound",
134            );
135    }
136
137    #[test]
138    fn length() {
139        let last_immutable_file_number = 10;
140
141        let immutable_file_range = ImmutableFileRange::Full;
142        assert_eq!(
143            last_immutable_file_number,
144            immutable_file_range.length(last_immutable_file_number)
145        );
146
147        let immutable_file_range = ImmutableFileRange::From(5);
148        assert_eq!(6, immutable_file_range.length(last_immutable_file_number));
149
150        let immutable_file_range = ImmutableFileRange::Range(5, 8);
151        assert_eq!(4, immutable_file_range.length(last_immutable_file_number));
152
153        let immutable_file_range = ImmutableFileRange::UpTo(8);
154        assert_eq!(8, immutable_file_range.length(last_immutable_file_number));
155    }
156}