hydrus_api/api_core/endpoints/
adding_tags.rs

1use crate::api_core::common::ServiceIdentifier;
2use crate::api_core::endpoints::Endpoint;
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, Deserialize)]
6pub struct CleanTagsResponse {
7    pub tags: Vec<String>,
8}
9
10pub struct CleanTags;
11
12impl Endpoint for CleanTags {
13    type Request = ();
14    type Response = CleanTagsResponse;
15
16    fn path() -> String {
17        String::from("add_tags/clean_tags")
18    }
19}
20
21#[derive(Debug, Clone, Serialize)]
22pub struct AddTagsRequest {
23    pub hashes: Vec<String>,
24    pub service_keys_to_tags: HashMap<String, Vec<String>>,
25    pub service_keys_to_actions_to_tags: HashMap<String, HashMap<String, Vec<String>>>,
26}
27
28pub struct AddTags;
29
30impl Endpoint for AddTags {
31    type Request = AddTagsRequest;
32    type Response = ();
33
34    fn path() -> String {
35        String::from("add_tags/add_tags")
36    }
37}
38
39#[derive(Default)]
40pub struct AddTagsRequestBuilder {
41    hashes: Vec<String>,
42    service_keys_to_tags: HashMap<String, Vec<String>>,
43    service_keys_to_actions_to_tags: HashMap<String, HashMap<String, Vec<String>>>,
44}
45
46/// List of actions for a given tag
47#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
48pub enum TagAction {
49    /// Add to a local tag service.
50    AddToLocalService,
51
52    /// Delete from a local tag service.
53    DeleteFromLocalService,
54
55    /// Pend to a tag repository.
56    PendAddToRepository,
57
58    ///  Rescind a pend from a tag repository.
59    RescindPendFromRepository,
60
61    /// Petition from a tag repository. (This is special)
62    PetitionFromRepository,
63
64    /// Rescind a petition from a tag repository.
65    RescindPetitionFromRepository,
66}
67
68impl Eq for TagAction {}
69
70impl TagAction {
71    fn into_id(self) -> u8 {
72        match self {
73            TagAction::AddToLocalService => 0,
74            TagAction::DeleteFromLocalService => 1,
75            TagAction::PendAddToRepository => 2,
76            TagAction::RescindPendFromRepository => 3,
77            TagAction::PetitionFromRepository => 4,
78            TagAction::RescindPetitionFromRepository => 5,
79        }
80    }
81}
82
83impl AddTagsRequestBuilder {
84    /// Adds a file hash to the request
85    pub fn add_hash<S: AsRef<str>>(mut self, hash: S) -> Self {
86        self.hashes.push(hash.as_ref().into());
87
88        self
89    }
90
91    /// Adds multiple file hashes to the request
92    pub fn add_hashes(mut self, mut hashes: Vec<String>) -> Self {
93        self.hashes.append(&mut hashes);
94
95        self
96    }
97
98    /// Adds a single tag for a given service
99    pub fn add_tag<S: AsRef<str>>(mut self, service_key: String, tag: S) -> Self {
100        let (service, relevant_mappings) = (service_key, &mut self.service_keys_to_tags);
101        if let Some(mappings) = relevant_mappings.get_mut(&service) {
102            mappings.push(tag.as_ref().into())
103        } else {
104            relevant_mappings.insert(service, vec![tag.as_ref().into()]);
105        }
106
107        self
108    }
109
110    /// Adds multiple tags for a given service
111    pub fn add_tags(mut self, service_key: String, mut tags: Vec<String>) -> Self {
112        let (service, relevant_mappings) = (service_key, &mut self.service_keys_to_tags);
113        if let Some(mappings) = relevant_mappings.get_mut(&service) {
114            mappings.append(&mut tags);
115        } else {
116            relevant_mappings.insert(service, tags);
117        }
118
119        self
120    }
121
122    /// Adds one tag for a given service with a defined action
123    pub fn add_tag_with_action<S: AsRef<str>>(
124        mut self,
125        service_key: String,
126        tag: S,
127        action: TagAction,
128    ) -> Self {
129        let (service, relevant_mappings) = (service_key, &mut self.service_keys_to_actions_to_tags);
130        let action_id = action.into_id();
131        if let Some(actions) = relevant_mappings.get_mut(&service) {
132            if let Some(tags) = actions.get_mut(&action_id.to_string()) {
133                tags.push(tag.as_ref().into());
134            } else {
135                actions.insert(action_id.to_string(), vec![tag.as_ref().into()]);
136            }
137        } else {
138            let mut actions = HashMap::new();
139            actions.insert(action_id.to_string(), vec![tag.as_ref().into()]);
140            relevant_mappings.insert(service, actions);
141        }
142        self
143    }
144
145    /// builds the request
146    pub fn build(self) -> AddTagsRequest {
147        AddTagsRequest {
148            hashes: self.hashes,
149            service_keys_to_tags: self.service_keys_to_tags,
150            service_keys_to_actions_to_tags: self.service_keys_to_actions_to_tags,
151        }
152    }
153}
154
155pub struct SearchTags;
156
157impl Endpoint for SearchTags {
158    type Request = ();
159
160    type Response = SearchTagsResponse;
161
162    fn path() -> String {
163        String::from("add_tags/search_tags")
164    }
165}
166
167#[derive(Debug, Deserialize)]
168pub struct SearchTagsResponse {
169    pub tags: Vec<TagWithCount>,
170}
171
172#[derive(Debug, Deserialize)]
173pub struct TagWithCount {
174    /// The name of the tag
175    pub value: String,
176    /// The count of how many times it was found in the database
177    pub count: u64,
178}
179
180#[derive(Debug, Default)]
181pub struct TagSearchOptions {
182    /// And optional filter for the service the tags should belong to
183    pub tag_service: Option<ServiceIdentifier>,
184    /// Controls how the tags in the result should be displayed
185    pub display_type: TagDisplayType,
186}
187
188#[derive(Debug)]
189pub enum TagDisplayType {
190    /// Returns tags as stored in the hydrus database
191    Storage,
192    /// Returns tags as displayed by hydrus
193    Display,
194}
195
196impl Default for TagDisplayType {
197    fn default() -> Self {
198        Self::Storage
199    }
200}
201
202impl TagDisplayType {
203    fn to_api_string(&self) -> &'static str {
204        match self {
205            TagDisplayType::Storage => "storage",
206            TagDisplayType::Display => "display",
207        }
208    }
209}
210
211impl TagSearchOptions {
212    /// Sets the display type of the search result
213    pub fn display_type(mut self, display_type: TagDisplayType) -> Self {
214        self.display_type = display_type;
215        self
216    }
217
218    /// Adds a filter for the tag service that the tags we're searching for
219    /// should belong to.
220    pub fn tag_service(mut self, tag_service: ServiceIdentifier) -> Self {
221        self.tag_service = Some(tag_service);
222        self
223    }
224
225    pub(crate) fn into_query_args(self) -> Vec<(&'static str, String)> {
226        let mut args = Vec::new();
227
228        if let Some(service) = self.tag_service {
229            match service {
230                ServiceIdentifier::Name(name) => args.push(("tag_service_name", name)),
231                ServiceIdentifier::Key(key) => args.push(("tag_service_key", key)),
232            }
233        }
234        args.push((
235            "tag_display_type",
236            self.display_type.to_api_string().to_string(),
237        ));
238
239        args
240    }
241}