hydrus_api/wrapper/builders/
import_builder.rs

1use crate::api_core::common::ServiceIdentifier;
2use crate::api_core::endpoints::adding_files::{STATUS_IMPORT_FAILED, STATUS_IMPORT_VETOED};
3use crate::api_core::endpoints::adding_urls::AddUrlRequestBuilder;
4use crate::error::{Error, Result};
5use crate::utils::tag_list_to_string_list;
6use crate::wrapper::hydrus_file::HydrusFile;
7use crate::wrapper::page::PageIdentifier;
8use crate::wrapper::tag::Tag;
9use crate::wrapper::url::Url;
10use crate::Client;
11use std::collections::HashMap;
12use std::io::Read;
13
14pub struct ImportBuilder {
15    pub(crate) client: Client,
16}
17
18impl ImportBuilder {
19    pub fn file(self, file: FileImport) -> FileImportBuilder {
20        FileImportBuilder {
21            client: self.client,
22            file,
23        }
24    }
25
26    pub fn url<S: ToString>(self, url: S) -> UrlImportBuilder {
27        UrlImportBuilder::new(self.client.clone(), url)
28    }
29}
30
31pub enum FileImport {
32    Path(String),
33    Binary(Vec<u8>),
34}
35
36impl FileImport {
37    pub fn path<S: ToString>(path: S) -> Self {
38        Self::Path(path.to_string())
39    }
40
41    pub fn binary<R: Read>(reader: &mut R) -> Self {
42        let mut bytes = Vec::new();
43        let _ = reader.read_to_end(&mut bytes);
44        Self::Binary(bytes)
45    }
46}
47
48pub struct FileImportBuilder {
49    client: Client,
50    file: FileImport,
51}
52
53impl FileImportBuilder {
54    pub async fn run(self) -> Result<HydrusFile> {
55        let response = match self.file {
56            FileImport::Path(path) => self.client.add_file(path).await?,
57            FileImport::Binary(b) => self.client.add_binary_file(b).await?,
58        };
59
60        if response.status == STATUS_IMPORT_FAILED {
61            Err(Error::ImportFailed(response.note))
62        } else if response.status == STATUS_IMPORT_VETOED {
63            Err(Error::ImportVetoed(response.note))
64        } else {
65            Ok(HydrusFile::from_raw_status_and_hash(
66                self.client,
67                response.status,
68                response.hash,
69            ))
70        }
71    }
72}
73
74pub struct UrlImportBuilder {
75    client: Client,
76    url: String,
77    page: Option<PageIdentifier>,
78    show_page: bool,
79    filter_tags: Vec<Tag>,
80    service_tag_mappings: HashMap<ServiceIdentifier, Vec<Tag>>,
81}
82
83impl UrlImportBuilder {
84    pub fn new<S: ToString>(client: Client, url: S) -> Self {
85        Self {
86            client,
87            url: url.to_string(),
88            page: None,
89            show_page: false,
90            filter_tags: vec![],
91            service_tag_mappings: Default::default(),
92        }
93    }
94
95    /// Sets the destination page of the import
96    pub fn page(mut self, page: PageIdentifier) -> Self {
97        self.page = Some(page);
98
99        self
100    }
101
102    /// If the destination page of the import should be focussed
103    pub fn show_page(mut self, show: bool) -> Self {
104        self.show_page = show;
105
106        self
107    }
108
109    /// Adds a tag that should be filtered
110    pub fn add_filter_tag(mut self, tag: Tag) -> Self {
111        self.filter_tags.push(tag);
112
113        self
114    }
115
116    /// Adds multiple tags that should be filtered
117    pub fn add_filter_tags(mut self, mut tags: Vec<Tag>) -> Self {
118        self.filter_tags.append(&mut tags);
119
120        self
121    }
122
123    /// Adds an additional tag for the imported file
124    pub fn add_additional_tag(self, service: ServiceIdentifier, tag: Tag) -> Self {
125        self.add_additional_tags(service, vec![tag])
126    }
127
128    /// Adds multiple additional tags for the import
129    pub fn add_additional_tags(mut self, service: ServiceIdentifier, mut tags: Vec<Tag>) -> Self {
130        if let Some(service_tags) = self.service_tag_mappings.get_mut(&service) {
131            service_tags.append(&mut tags);
132        } else {
133            self.service_tag_mappings.insert(service, tags);
134        }
135
136        self
137    }
138
139    /// Imports the URL
140    pub async fn run(self) -> Result<Url> {
141        let mut request = AddUrlRequestBuilder::default().url(&self.url);
142
143        for (service, tags) in self.service_tag_mappings {
144            request = request.add_tags(service, tag_list_to_string_list(tags));
145        }
146        request = request.add_filter_tags(tag_list_to_string_list(self.filter_tags));
147        if let Some(page) = self.page {
148            request = match page {
149                PageIdentifier::Name(n) => request.destination_page_name(n),
150                PageIdentifier::Key(k) => request.destination_page_key(k),
151            };
152        }
153        request = request.show_destination_page(self.show_page);
154
155        let response = self.client.add_url(request.build()).await?;
156        let url_info = self.client.get_url_info(&self.url).await?;
157
158        Ok(Url {
159            url: self.url,
160            client: self.client,
161            normalised_url: response.normalised_url,
162            url_type: url_info.url_type.into(),
163            match_name: url_info.match_name,
164            can_parse: url_info.can_parse,
165        })
166    }
167}