1#![forbid(unsafe_code)]
2mod cache;
21mod engine;
22mod index;
23mod items;
24mod record;
25mod snapshot;
26mod tasks;
27#[cfg(test)]
28mod tests;
29pub mod warc;
30
31use std::fmt;
32use std::io;
33use std::path::PathBuf;
34use std::time::{Duration, SystemTime, UNIX_EPOCH};
35
36pub use cache::{Cache, CacheEntry, CacheLookup, CachePolicy};
37pub use engine::Store;
38pub use items::Items;
39pub use snapshot::{ContentHash, SnapshotMeta, SnapshotPut, Snapshots};
40pub use tasks::Tasks;
41pub use warc::{
42 AssetSniffer, RecordType, SubresourceAsset, WarcDigests, WarcRecord, WarcWriter,
43 WebpageSnapshot,
44};
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum SyncPolicy {
49 Always,
51 Interval(Duration),
54 Never,
56}
57
58#[derive(Debug, Clone)]
60#[non_exhaustive]
61pub struct Config {
62 pub dir: PathBuf,
63 pub sync: SyncPolicy,
64 pub max_segment_bytes: u64,
66 pub max_bytes: Option<u64>,
68 pub low_watermark: f64,
70 pub compact_ratio: f64,
72 pub compact_min_bytes: u64,
74 pub background: bool,
76 pub snapshot_max_bytes: usize,
78 pub snapshot_zstd_level: i32,
79 pub(crate) hash_mask: u64,
81}
82
83impl Config {
84 pub fn new(dir: impl Into<PathBuf>) -> Self {
85 Self {
86 dir: dir.into(),
87 sync: SyncPolicy::Interval(Duration::from_secs(1)),
88 max_segment_bytes: 64 << 20,
89 max_bytes: None,
90 low_watermark: 0.9,
91 compact_ratio: 0.5,
92 compact_min_bytes: 16 << 20,
93 background: true,
94 snapshot_max_bytes: 512 << 10,
95 snapshot_zstd_level: 3,
96 hash_mask: u64::MAX,
97 }
98 }
99}
100
101#[derive(Debug)]
102#[non_exhaustive]
103pub enum Error {
104 Io(io::Error),
105 Corrupt(String),
107 KeyTooLarge(usize),
109 ValueTooLarge {
111 size: usize,
112 limit: usize,
113 },
114}
115
116impl Error {
117 pub fn is_locked(&self) -> bool {
119 matches!(self, Error::Io(e) if e.kind() == io::ErrorKind::WouldBlock)
120 }
121}
122
123impl fmt::Display for Error {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 match self {
126 Error::Io(e) => write!(f, "store io error: {e}"),
127 Error::Corrupt(m) => write!(f, "store corrupt: {m}"),
128 Error::KeyTooLarge(n) => write!(f, "key too large: {n} bytes"),
129 Error::ValueTooLarge { size, limit } => {
130 write!(f, "value too large: {size} bytes (limit {limit})")
131 }
132 }
133 }
134}
135
136impl std::error::Error for Error {
137 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
138 match self {
139 Error::Io(e) => Some(e),
140 _ => None,
141 }
142 }
143}
144
145impl From<io::Error> for Error {
146 fn from(e: io::Error) -> Self {
147 Error::Io(e)
148 }
149}
150
151pub type Result<T, E = Error> = std::result::Result<T, E>;
152
153#[derive(Debug, Clone, Default)]
155#[non_exhaustive]
156pub struct OpenReport {
157 pub segments: usize,
158 pub records: u64,
159 pub truncated_bytes: u64,
161 pub elapsed: Duration,
162}
163
164#[derive(Debug, Clone, Default)]
165#[non_exhaustive]
166pub struct Stats {
167 pub entries: u64,
168 pub index_bytes: u64,
170 pub disk_bytes: u64,
171 pub live_bytes: u64,
172 pub segments: usize,
173 pub open: OpenReport,
174 pub last_error: Option<String>,
176}
177
178#[derive(Debug, Clone, Default)]
179#[non_exhaustive]
180pub struct GcReport {
181 pub evicted_entries: u64,
182 pub evicted_bytes: u64,
183 pub compacted_segments: usize,
184 pub disk_bytes_before: u64,
185 pub disk_bytes_after: u64,
186}
187
188pub(crate) fn to_ms(t: SystemTime) -> u64 {
189 t.duration_since(UNIX_EPOCH)
190 .map_or(0, |d| d.as_millis() as u64)
191}
192
193pub(crate) fn from_ms(ms: u64) -> SystemTime {
194 UNIX_EPOCH + Duration::from_millis(ms)
195}
196
197pub(crate) fn now_secs() -> u32 {
198 (to_ms(SystemTime::now()) / 1000) as u32
199}