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::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
90#[derive(Clone, Debug)]
91pub enum FudEvent {
92    DownloadStarted(DownloadStarted),
93    ChunkDownloadCompleted(ChunkDownloadCompleted),
94    MetadataDownloadCompleted(MetadataDownloadCompleted),
95    DownloadCompleted(DownloadCompleted),
96    ResourceUpdated(ResourceUpdated),
97    ResourceRemoved(ResourceRemoved),
98    ChunkNotFound(ChunkNotFound),
99    MetadataNotFound(MetadataNotFound),
100    MissingChunks(MissingChunks),
101    DownloadError(DownloadError),
102    InsertCompleted(InsertCompleted),
103    InsertError(InsertError),
104}
105
106impl From<DownloadStarted> for JsonValue {
107    fn from(info: DownloadStarted) -> JsonValue {
108        json_map([
109            ("hash", JsonValue::String(hash_to_string(&info.hash))),
110            ("resource", info.resource.into()),
111        ])
112    }
113}
114impl From<ChunkDownloadCompleted> for JsonValue {
115    fn from(info: ChunkDownloadCompleted) -> JsonValue {
116        json_map([
117            ("hash", JsonValue::String(hash_to_string(&info.hash))),
118            ("chunk_hash", JsonValue::String(hash_to_string(&info.chunk_hash))),
119            ("resource", info.resource.into()),
120        ])
121    }
122}
123impl From<MetadataDownloadCompleted> for JsonValue {
124    fn from(info: MetadataDownloadCompleted) -> JsonValue {
125        json_map([
126            ("hash", JsonValue::String(hash_to_string(&info.hash))),
127            ("resource", info.resource.into()),
128        ])
129    }
130}
131impl From<DownloadCompleted> for JsonValue {
132    fn from(info: DownloadCompleted) -> JsonValue {
133        json_map([
134            ("hash", JsonValue::String(hash_to_string(&info.hash))),
135            ("resource", info.resource.into()),
136        ])
137    }
138}
139impl From<ResourceUpdated> for JsonValue {
140    fn from(info: ResourceUpdated) -> JsonValue {
141        json_map([
142            ("hash", JsonValue::String(hash_to_string(&info.hash))),
143            ("resource", info.resource.into()),
144        ])
145    }
146}
147impl From<ResourceRemoved> for JsonValue {
148    fn from(info: ResourceRemoved) -> JsonValue {
149        json_map([("hash", JsonValue::String(hash_to_string(&info.hash)))])
150    }
151}
152impl From<ChunkNotFound> for JsonValue {
153    fn from(info: ChunkNotFound) -> JsonValue {
154        json_map([
155            ("hash", JsonValue::String(hash_to_string(&info.hash))),
156            ("chunk_hash", JsonValue::String(hash_to_string(&info.chunk_hash))),
157        ])
158    }
159}
160impl From<MetadataNotFound> for JsonValue {
161    fn from(info: MetadataNotFound) -> JsonValue {
162        json_map([
163            ("hash", JsonValue::String(hash_to_string(&info.hash))),
164            ("resource", info.resource.into()),
165        ])
166    }
167}
168impl From<MissingChunks> for JsonValue {
169    fn from(info: MissingChunks) -> JsonValue {
170        json_map([
171            ("hash", JsonValue::String(hash_to_string(&info.hash))),
172            ("resource", info.resource.into()),
173        ])
174    }
175}
176impl From<DownloadError> for JsonValue {
177    fn from(info: DownloadError) -> JsonValue {
178        json_map([
179            ("hash", JsonValue::String(hash_to_string(&info.hash))),
180            ("error", JsonValue::String(info.error)),
181        ])
182    }
183}
184impl From<InsertCompleted> for JsonValue {
185    fn from(info: InsertCompleted) -> JsonValue {
186        json_map([
187            ("hash", JsonValue::String(hash_to_string(&info.hash))),
188            ("path", JsonValue::String(info.path.to_string_lossy().to_string())),
189        ])
190    }
191}
192impl From<InsertError> for JsonValue {
193    fn from(info: InsertError) -> JsonValue {
194        json_map([
195            ("path", JsonValue::String(info.path.to_string_lossy().to_string())),
196            ("error", JsonValue::String(info.error)),
197        ])
198    }
199}
200impl From<FudEvent> for JsonValue {
201    fn from(event: FudEvent) -> JsonValue {
202        match event {
203            FudEvent::DownloadStarted(info) => {
204                json_map([("event", json_str("download_started")), ("info", info.into())])
205            }
206            FudEvent::ChunkDownloadCompleted(info) => {
207                json_map([("event", json_str("chunk_download_completed")), ("info", info.into())])
208            }
209            FudEvent::MetadataDownloadCompleted(info) => json_map([
210                ("event", json_str("metadata_download_completed")),
211                ("info", info.into()),
212            ]),
213            FudEvent::DownloadCompleted(info) => {
214                json_map([("event", json_str("download_completed")), ("info", info.into())])
215            }
216            FudEvent::ResourceUpdated(info) => {
217                json_map([("event", json_str("resource_updated")), ("info", info.into())])
218            }
219            FudEvent::ResourceRemoved(info) => {
220                json_map([("event", json_str("resource_removed")), ("info", info.into())])
221            }
222            FudEvent::ChunkNotFound(info) => {
223                json_map([("event", json_str("chunk_not_found")), ("info", info.into())])
224            }
225            FudEvent::MetadataNotFound(info) => {
226                json_map([("event", json_str("metadata_not_found")), ("info", info.into())])
227            }
228            FudEvent::MissingChunks(info) => {
229                json_map([("event", json_str("missing_chunks")), ("info", info.into())])
230            }
231            FudEvent::DownloadError(info) => {
232                json_map([("event", json_str("download_error")), ("info", info.into())])
233            }
234            FudEvent::InsertCompleted(info) => {
235                json_map([("event", json_str("insert_completed")), ("info", info.into())])
236            }
237            FudEvent::InsertError(info) => {
238                json_map([("event", json_str("insert_error")), ("info", info.into())])
239            }
240        }
241    }
242}
243
244/// Macro calling `fud.event_publisher.notify()`
245macro_rules! notify_event {
246    // This is for any `FudEvent`
247    ($fud:ident, $event:ident, { $($fields:tt)* }) => {
248        $fud
249            .event_publisher
250            .notify(FudEvent::$event(event::$event {
251                $($fields)*
252            }))
253            .await;
254    };
255    // This is for `FudEvent`s that only have a hash and resource
256    ($fud:ident, $event:ident, $resource:expr) => {
257        $fud
258            .event_publisher
259            .notify(FudEvent::$event(event::$event {
260                hash: $resource.hash,
261                resource: $resource.clone(),
262            }))
263            .await;
264    };
265}
266pub(crate) use notify_event;