syncweb_core/sync/
subscribe.rs1use std::path::{Path, PathBuf};
2
3use iroh_blobs::Hash;
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7use crate::schedule::BandwidthLimits;
8
9#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
11#[non_exhaustive]
12pub enum AreaFilter {
13 All,
14 Prefix(PathBuf),
15 Glob(String),
16 HashRange([u8; 32], [u8; 32]),
17}
18
19impl AreaFilter {
20 #[must_use]
21 pub fn matches_path(&self, path: &Path) -> bool {
22 match self {
23 Self::Prefix(prefix) => path.starts_with(prefix),
24 Self::Glob(pattern) => globset::Glob::new(pattern).is_ok_and(|glob| glob.compile_matcher().is_match(path)),
25 Self::All | Self::HashRange(_, _) => true,
26 }
27 }
28
29 #[must_use]
30 pub fn matches_hash(&self, hash: &Hash) -> bool {
31 match self {
32 Self::All | Self::Prefix(_) | Self::Glob(_) => true,
33 Self::HashRange(start, end) => {
34 let bytes = hash.as_bytes();
35 bytes >= start && bytes < end
36 }
37 }
38 }
39
40 #[must_use]
41 pub fn matches_entry(&self, path: &Path, hash: &Hash) -> bool {
42 self.matches_path(path) && self.matches_hash(hash)
43 }
44}
45
46#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
48#[non_exhaustive]
49pub struct SubscribeParams {
50 pub ingest_only: bool,
51 pub ignore_session: Option<Uuid>,
52 pub area_filter: Option<AreaFilter>,
53 pub area_of_interest: Option<AreaOfInterest>,
54 pub bandwidth: Option<BandwidthLimits>,
55}
56
57impl SubscribeParams {
58 #[must_use]
59 pub const fn ingest_only() -> Self {
60 Self {
61 ingest_only: true,
62 ignore_session: None,
63 area_filter: None,
64 area_of_interest: None,
65 bandwidth: None,
66 }
67 }
68
69 #[must_use]
70 pub const fn ignore_session(session_id: Uuid) -> Self {
71 Self {
72 ingest_only: false,
73 ignore_session: Some(session_id),
74 area_filter: None,
75 area_of_interest: None,
76 bandwidth: None,
77 }
78 }
79
80 #[must_use]
81 pub fn with_area(mut self, area_filter: AreaFilter) -> Self {
82 self.area_filter = Some(area_filter);
83 self
84 }
85
86 #[must_use]
87 pub fn with_limits(mut self, area: AreaOfInterest) -> Self {
88 self.area_of_interest = Some(area);
89 self
90 }
91
92 #[must_use]
93 pub const fn with_bandwidth_limits(mut self, bandwidth: BandwidthLimits) -> Self {
94 self.bandwidth = Some(bandwidth);
95 self
96 }
97
98 #[must_use]
99 pub fn accepts(&self, path: &Path, hash: &Hash) -> bool {
100 let area_matches = self
101 .area_of_interest
102 .as_ref()
103 .is_none_or(|area| area.area.matches_entry(path, hash));
104 let filter_matches = self
105 .area_filter
106 .as_ref()
107 .is_none_or(|filter| filter.matches_entry(path, hash));
108 area_matches && filter_matches
109 }
110}
111
112#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
114#[non_exhaustive]
115pub struct AreaOfInterest {
116 pub area: AreaFilter,
117 pub max_size: u64,
118 pub max_count: u64,
119}
120
121impl AreaOfInterest {
122 #[must_use]
123 pub const fn unlimited(area: AreaFilter) -> Self {
124 Self {
125 area,
126 max_size: 0,
127 max_count: 0,
128 }
129 }
130
131 #[must_use]
132 pub const fn with_count_limit(area: AreaFilter, max_count: u64) -> Self {
133 Self {
134 area,
135 max_size: 0,
136 max_count,
137 }
138 }
139
140 #[must_use]
141 pub const fn with_size_limit(area: AreaFilter, max_size: u64) -> Self {
142 Self {
143 area,
144 max_size,
145 max_count: 0,
146 }
147 }
148
149 #[must_use]
150 pub const fn with_limits(area: AreaFilter, max_size: u64, max_count: u64) -> Self {
151 Self {
152 area,
153 max_size,
154 max_count,
155 }
156 }
157
158 #[must_use]
159 pub const fn is_limit_reached(&self, synced_count: u64, synced_size: u64) -> bool {
160 (self.max_count > 0 && synced_count >= self.max_count) || (self.max_size > 0 && synced_size >= self.max_size)
161 }
162
163 #[must_use]
164 pub const fn permits(&self, synced_count: u64, synced_size: u64, next_size: u64) -> bool {
165 let next_count = synced_count.saturating_add(1);
166 let next_total_size = synced_size.saturating_add(next_size);
167 (self.max_count == 0 || next_count <= self.max_count)
168 && (self.max_size == 0 || next_total_size <= self.max_size)
169 }
170}