fud/
event.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use std::path::PathBuf;
20use tinyjson::JsonValue;
21
22use darkfi::{
23    geode::hash_to_string,
24    rpc::util::{json_map, json_str},
25};
26
27use crate::{dht::FudSeeder, resource::Resource};
28
29#[derive(Clone, Debug)]
30pub struct DownloadStarted {
31    pub hash: blake3::Hash,
32    pub resource: Resource,
33}
34#[derive(Clone, Debug)]
35pub struct ChunkDownloadCompleted {
36    pub hash: blake3::Hash,
37    pub chunk_hash: blake3::Hash,
38    pub resource: Resource,
39}
40#[derive(Clone, Debug)]
41pub struct MetadataDownloadCompleted {
42    pub hash: blake3::Hash,
43    pub resource: Resource,
44}
45#[derive(Clone, Debug)]
46pub struct DownloadCompleted {
47    pub hash: blake3::Hash,
48    pub resource: Resource,
49}
50#[derive(Clone, Debug)]
51pub struct ResourceUpdated {
52    pub hash: blake3::Hash,
53    pub resource: Resource,
54}
55#[derive(Clone, Debug)]
56pub struct ResourceRemoved {
57    pub hash: blake3::Hash,
58}
59#[derive(Clone, Debug)]
60pub struct ChunkNotFound {
61    pub hash: blake3::Hash,
62    pub chunk_hash: blake3::Hash,
63}
64#[derive(Clone, Debug)]
65pub struct MetadataNotFound {
66    pub hash: blake3::Hash,
67    pub resource: Resource,
68}
69#[derive(Clone, Debug)]
70pub struct MissingChunks {
71    pub hash: blake3::Hash,
72    pub resource: Resource,
73}
74#[derive(Clone, Debug)]
75pub struct DownloadError {
76    pub hash: blake3::Hash,
77    pub error: String,
78}
79#[derive(Clone, Debug)]
80pub struct InsertCompleted {
81    pub hash: blake3::Hash,
82    pub path: PathBuf,
83}
84#[derive(Clone, Debug)]
85pub struct InsertError {
86    pub path: PathBuf,
87    pub error: String,
88}
89#[derive(Clone, Debug)]
90pub struct SeedersFound {
91    pub hash: blake3::Hash,
92    pub seeders: Vec<FudSeeder>,
93}
94
95#[derive(Clone, Debug)]
96pub enum FudEvent {
97    Ready,
98    DownloadStarted(DownloadStarted),
99    ChunkDownloadCompleted(ChunkDownloadCompleted),
100    MetadataDownloadCompleted(MetadataDownloadCompleted),
101    DownloadCompleted(DownloadCompleted),
102    ResourceUpdated(ResourceUpdated),
103    ResourceRemoved(ResourceRemoved),
104    ChunkNotFound(ChunkNotFound),
105    MetadataNotFound(MetadataNotFound),
106    MissingChunks(MissingChunks),
107    DownloadError(DownloadError),
108    InsertCompleted(InsertCompleted),
109    InsertError(InsertError),
110    SeedersFound(SeedersFound),
111}
112
113impl From<DownloadStarted> for JsonValue {
114    fn from(info: DownloadStarted) -> JsonValue {
115        json_map([
116            ("hash", JsonValue::String(hash_to_string(&info.hash))),
117            ("resource", info.resource.into()),
118        ])
119    }
120}
121impl From<ChunkDownloadCompleted> for JsonValue {
122    fn from(info: ChunkDownloadCompleted) -> JsonValue {
123        json_map([
124            ("hash", JsonValue::String(hash_to_string(&info.hash))),
125            ("chunk_hash", JsonValue::String(hash_to_string(&info.chunk_hash))),
126            ("resource", info.resource.into()),
127        ])
128    }
129}
130impl From<MetadataDownloadCompleted> for JsonValue {
131    fn from(info: MetadataDownloadCompleted) -> JsonValue {
132        json_map([
133            ("hash", JsonValue::String(hash_to_string(&info.hash))),
134            ("resource", info.resource.into()),
135        ])
136    }
137}
138impl From<DownloadCompleted> for JsonValue {
139    fn from(info: DownloadCompleted) -> JsonValue {
140        json_map([
141            ("hash", JsonValue::String(hash_to_string(&info.hash))),
142            ("resource", info.resource.into()),
143        ])
144    }
145}
146impl From<ResourceUpdated> for JsonValue {
147    fn from(info: ResourceUpdated) -> JsonValue {
148        json_map([
149            ("hash", JsonValue::String(hash_to_string(&info.hash))),
150            ("resource", info.resource.into()),
151        ])
152    }
153}
154impl From<ResourceRemoved> for JsonValue {
155    fn from(info: ResourceRemoved) -> JsonValue {
156        json_map([("hash", JsonValue::String(hash_to_string(&info.hash)))])
157    }
158}
159impl From<ChunkNotFound> for JsonValue {
160    fn from(info: ChunkNotFound) -> JsonValue {
161        json_map([
162            ("hash", JsonValue::String(hash_to_string(&info.hash))),
163            ("chunk_hash", JsonValue::String(hash_to_string(&info.chunk_hash))),
164        ])
165    }
166}
167impl From<MetadataNotFound> for JsonValue {
168    fn from(info: MetadataNotFound) -> JsonValue {
169        json_map([
170            ("hash", JsonValue::String(hash_to_string(&info.hash))),
171            ("resource", info.resource.into()),
172        ])
173    }
174}
175impl From<MissingChunks> for JsonValue {
176    fn from(info: MissingChunks) -> JsonValue {
177        json_map([
178            ("hash", JsonValue::String(hash_to_string(&info.hash))),
179            ("resource", info.resource.into()),
180        ])
181    }
182}
183impl From<DownloadError> for JsonValue {
184    fn from(info: DownloadError) -> JsonValue {
185        json_map([
186            ("hash", JsonValue::String(hash_to_string(&info.hash))),
187            ("error", JsonValue::String(info.error)),
188        ])
189    }
190}
191impl From<InsertCompleted> for JsonValue {
192    fn from(info: InsertCompleted) -> JsonValue {
193        json_map([
194            ("hash", JsonValue::String(hash_to_string(&info.hash))),
195            ("path", JsonValue::String(info.path.to_string_lossy().to_string())),
196        ])
197    }
198}
199impl From<InsertError> for JsonValue {
200    fn from(info: InsertError) -> JsonValue {
201        json_map([
202            ("path", JsonValue::String(info.path.to_string_lossy().to_string())),
203            ("error", JsonValue::String(info.error)),
204        ])
205    }
206}
207impl From<SeedersFound> for JsonValue {
208    fn from(info: SeedersFound) -> JsonValue {
209        json_map([
210            ("hash", JsonValue::String(hash_to_string(&info.hash))),
211            (
212                "seeders",
213                JsonValue::Array(info.seeders.iter().map(|seeder| seeder.clone().into()).collect()),
214            ),
215        ])
216    }
217}
218impl From<FudEvent> for JsonValue {
219    fn from(event: FudEvent) -> JsonValue {
220        match event {
221            FudEvent::Ready => json_map([("event", json_str("ready"))]),
222            FudEvent::DownloadStarted(info) => {
223                json_map([("event", json_str("download_started")), ("info", info.into())])
224            }
225            FudEvent::ChunkDownloadCompleted(info) => {
226                json_map([("event", json_str("chunk_download_completed")), ("info", info.into())])
227            }
228            FudEvent::MetadataDownloadCompleted(info) => json_map([
229                ("event", json_str("metadata_download_completed")),
230                ("info", info.into()),
231            ]),
232            FudEvent::DownloadCompleted(info) => {
233                json_map([("event", json_str("download_completed")), ("info", info.into())])
234            }
235            FudEvent::ResourceUpdated(info) => {
236                json_map([("event", json_str("resource_updated")), ("info", info.into())])
237            }
238            FudEvent::ResourceRemoved(info) => {
239                json_map([("event", json_str("resource_removed")), ("info", info.into())])
240            }
241            FudEvent::ChunkNotFound(info) => {
242                json_map([("event", json_str("chunk_not_found")), ("info", info.into())])
243            }
244            FudEvent::MetadataNotFound(info) => {
245                json_map([("event", json_str("metadata_not_found")), ("info", info.into())])
246            }
247            FudEvent::MissingChunks(info) => {
248                json_map([("event", json_str("missing_chunks")), ("info", info.into())])
249            }
250            FudEvent::DownloadError(info) => {
251                json_map([("event", json_str("download_error")), ("info", info.into())])
252            }
253            FudEvent::InsertCompleted(info) => {
254                json_map([("event", json_str("insert_completed")), ("info", info.into())])
255            }
256            FudEvent::InsertError(info) => {
257                json_map([("event", json_str("insert_error")), ("info", info.into())])
258            }
259            FudEvent::SeedersFound(info) => {
260                json_map([("event", json_str("seeders_found")), ("info", info.into())])
261            }
262        }
263    }
264}
265
266/// Macro calling `fud.event_publisher.notify()`
267macro_rules! notify_event {
268    // This is for any `FudEvent`
269    ($fud:expr, $event:ident, { $($fields:tt)* }) => {
270        $fud
271            .event_publisher
272            .notify(FudEvent::$event(event::$event {
273                $($fields)*
274            }))
275            .await;
276    };
277    // This is for `FudEvent`s that only have a hash and resource
278    ($fud:expr, $event:ident, $resource:expr) => {
279        $fud
280            .event_publisher
281            .notify(FudEvent::$event(event::$event {
282                hash: $resource.hash,
283                resource: $resource.clone(),
284            }))
285            .await;
286    };
287    // This is for `FudEvent`s with no fields
288    ($fud:expr, $event:ident) => {
289        $fud
290            .event_publisher
291            .notify(FudEvent::$event)
292            .await;
293    };
294}
295pub(crate) use notify_event;