Skip to main content

archive_it_client/models/
wasapi.rs

1use http_ferry::{Checksum, Source};
2use serde::Deserialize;
3
4#[derive(Debug, Clone, Deserialize)]
5pub struct Page<T> {
6    pub count: u64,
7    pub next: Option<String>,
8    pub previous: Option<String>,
9    pub files: Vec<T>,
10    #[serde(rename = "includes-extra")]
11    pub includes_extra: Option<bool>,
12    #[serde(rename = "request-url")]
13    pub request_url: Option<String>,
14}
15
16#[derive(Debug, Clone, Deserialize)]
17#[serde(rename_all = "kebab-case")]
18pub struct WasapiFile {
19    pub filename: String,
20    pub filetype: String,
21    pub checksums: Checksums,
22    pub account: u64,
23    pub size: u64,
24    pub collection: u64,
25    pub crawl: Option<u64>,
26    pub crawl_time: Option<String>,
27    pub crawl_start: Option<String>,
28    pub store_time: String,
29    pub locations: Vec<String>,
30}
31
32/// Projects a WASAPI file onto the fields the engine needs. The filename
33/// doubles as the label in `DownloadOutcome`'s `Display`. WASAPI supplies a
34/// sha1 (md5 is an empty placeholder in practice and ignored), so only sha1 is
35/// surfaced as the expected checksum.
36impl Source for WasapiFile {
37    fn name(&self) -> &str {
38        &self.filename
39    }
40
41    fn size(&self) -> u64 {
42        self.size
43    }
44
45    fn checksum(&self) -> Option<Checksum> {
46        self.checksums.sha1.clone().map(Checksum::Sha1)
47    }
48}
49
50#[derive(Debug, Clone, Deserialize)]
51pub struct Checksums {
52    pub sha1: Option<String>,
53    pub md5: Option<String>,
54}
55
56#[cfg(test)]
57mod tests {
58    use super::WasapiFile;
59
60    #[test]
61    fn wasapi_file_allows_null_crawl_fields() {
62        let file: WasapiFile = serde_json::from_value(serde_json::json!({
63            "filename": "ARCHIVEIT-1.warc.gz",
64            "filetype": "warc",
65            "checksums": { "sha1": "sha1", "md5": "md5" },
66            "account": 1,
67            "size": 123,
68            "collection": 4472,
69            "crawl": null,
70            "crawl-time": null,
71            "crawl-start": null,
72            "store-time": "2025-01-01T00:00:00Z",
73            "locations": ["https://example.invalid/warcs/foo.warc.gz"]
74        }))
75        .unwrap();
76
77        assert_eq!(file.filename, "ARCHIVEIT-1.warc.gz");
78        assert_eq!(file.size, 123);
79        assert_eq!(file.account, 1);
80        assert_eq!(file.collection, 4472);
81        assert_eq!(file.crawl, None);
82        assert_eq!(file.crawl_time, None);
83        assert_eq!(file.crawl_start, None);
84        assert_eq!(file.store_time, "2025-01-01T00:00:00Z");
85    }
86
87    #[test]
88    fn wasapi_file_allows_missing_sha1_when_md5_is_present() {
89        let file: WasapiFile = serde_json::from_value(serde_json::json!({
90            "filename": "ARCHIVEIT-1.warc.gz",
91            "filetype": "warc",
92            "checksums": { "sha1": null, "md5": "md5" },
93            "account": 1,
94            "size": 123,
95            "collection": 4472,
96            "crawl": null,
97            "crawl-time": "2025-01-01T00:00:00Z",
98            "crawl-start": null,
99            "store-time": "2025-01-01T00:00:00Z",
100            "locations": ["https://example.invalid/warcs/foo.warc.gz"]
101        }))
102        .unwrap();
103
104        assert_eq!(file.checksums.sha1, None);
105        assert_eq!(file.checksums.md5.as_deref(), Some("md5"));
106    }
107}