1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6#[cfg(feature = "cli")]
7use structopt::StructOpt;
8use time::OffsetDateTime;
9
10use crate::{errors, is_default, UserAccountID};
11
12#[cfg_attr(feature = "cli", derive(StructOpt))]
13#[cfg_attr(
14 feature = "cli",
15 structopt(
16 about = "Create an annotation",
17 long_about = "Create and upload an annotation to your Hypothesis"
18 )
19)]
20#[derive(Serialize, Debug, Default, Clone, Builder, PartialEq)]
48#[builder(default, build_fn(name = "builder"))]
49pub struct InputAnnotation {
50 #[serde(skip_serializing_if = "is_default")]
55 #[cfg_attr(feature = "cli", structopt(default_value))]
56 #[builder(setter(into))]
57 pub uri: String,
58 #[serde(skip_serializing_if = "is_default")]
62 #[cfg_attr(feature = "cli", structopt(default_value, long))]
63 #[builder(setter(into))]
64 pub text: String,
65 #[serde(skip_serializing_if = "is_default")]
67 #[cfg_attr(feature = "cli", structopt(long))]
68 #[builder(setter(strip_option), default)]
69 pub tags: Option<Vec<String>>,
70 #[serde(skip_serializing_if = "is_default")]
72 #[cfg_attr(feature = "cli", structopt(skip))]
73 #[builder(setter(strip_option), default)]
74 pub document: Option<Document>,
75 #[serde(skip_serializing_if = "is_default")]
76 #[cfg_attr(feature = "cli", structopt(default_value, long))]
82 #[builder(setter(into))]
83 pub group: String,
84 #[serde(skip_serializing_if = "is_default")]
88 #[cfg_attr(feature = "cli", structopt(skip))]
89 pub target: Target,
90 #[serde(skip_serializing_if = "is_default")]
92 #[cfg_attr(feature = "cli", structopt(long))]
93 pub references: Vec<String>,
94}
95
96impl InputAnnotation {
97 pub fn builder() -> InputAnnotationBuilder {
98 InputAnnotationBuilder::default()
99 }
100}
101
102impl InputAnnotationBuilder {
103 pub fn build(&self) -> Result<InputAnnotation, errors::HypothesisError> {
105 self.builder()
106 .map_err(|e| errors::HypothesisError::BuilderError(e.to_string()))
107 }
108}
109
110impl Annotation {
111 pub fn update(&mut self, annotation: InputAnnotation) {
112 if !annotation.uri.is_empty() {
113 self.uri = annotation.uri;
114 }
115 if !annotation.text.is_empty() {
116 self.text = annotation.text;
117 }
118 if let Some(tags) = annotation.tags {
119 self.tags = tags;
120 }
121 if !annotation.group.is_empty() {
122 self.group = annotation.group;
123 }
124 if annotation.references.is_empty() {
125 self.references = annotation.references;
126 }
127 }
128}
129
130#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Builder)]
131#[builder(build_fn(name = "builder"))]
132pub struct Document {
133 #[serde(skip_serializing_if = "is_default", default)]
134 pub title: Vec<String>,
135 #[serde(skip_serializing_if = "is_default", default)]
136 #[builder(setter(strip_option), default)]
137 pub dc: Option<Dc>,
138 #[serde(skip_serializing_if = "is_default", default)]
139 #[builder(setter(strip_option), default)]
140 pub highwire: Option<HighWire>,
141 #[serde(skip_serializing_if = "is_default", default)]
142 pub link: Vec<Link>,
143}
144
145impl Document {
146 pub fn builder() -> DocumentBuilder {
147 DocumentBuilder::default()
148 }
149}
150
151impl DocumentBuilder {
152 pub fn build(&self) -> Result<Document, errors::HypothesisError> {
154 self.builder()
155 .map_err(|e| errors::HypothesisError::BuilderError(e.to_string()))
156 }
157}
158
159#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
160pub struct HighWire {
161 #[serde(skip_serializing_if = "is_default", default)]
162 pub doi: Vec<String>,
163 #[serde(skip_serializing_if = "is_default", default)]
164 pub pdf_url: Vec<String>,
165}
166
167#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
168pub struct Link {
169 pub href: String,
170 #[serde(skip_serializing_if = "is_default", rename = "type", default)]
171 pub link_type: String,
172}
173
174#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
175pub struct Dc {
176 #[serde(skip_serializing_if = "is_default", default)]
177 pub identifier: Vec<String>,
178}
179
180#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
182pub struct Annotation {
183 pub id: String,
185 #[serde(with = "time::serde::rfc3339")]
187 pub created: OffsetDateTime,
188 #[serde(with = "time::serde::rfc3339")]
190 pub updated: OffsetDateTime,
191 pub user: UserAccountID,
193 pub uri: String,
195 pub text: String,
197 pub tags: Vec<String>,
199 pub group: String,
201 pub permissions: Permissions,
202 pub target: Vec<Target>,
204 pub links: HashMap<String, String>,
206 pub hidden: bool,
208 pub flagged: bool,
210 #[serde(default)]
212 pub document: Option<Document>,
213 #[serde(default)]
215 pub references: Vec<String>,
216 #[serde(default)]
217 pub user_info: Option<UserInfo>,
218}
219
220#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
221pub struct UserInfo {
222 pub display_name: Option<String>,
224}
225
226#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Builder)]
230#[builder(build_fn(name = "builder"))]
231pub struct Target {
232 #[serde(skip_serializing_if = "is_default")]
235 #[builder(setter(into))]
236 pub source: String,
237 #[serde(default, skip_serializing_if = "is_default")]
239 pub selector: Vec<Selector>,
240}
241
242impl Target {
243 pub fn builder() -> TargetBuilder {
244 TargetBuilder::default()
245 }
246}
247
248impl TargetBuilder {
249 pub fn build(&self) -> Result<Target, errors::HypothesisError> {
251 self.builder()
252 .map_err(|e| errors::HypothesisError::BuilderError(e.to_string()))
253 }
254}
255
256#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
261#[serde(tag = "type")]
262pub enum Selector {
263 TextQuoteSelector(TextQuoteSelector),
264 TextPositionSelector(TextPositionSelector),
274 RangeSelector(HashMap<String, serde_json::Value>),
276 FragmentSelector(HashMap<String, serde_json::Value>),
277 CssSelector(HashMap<String, serde_json::Value>),
278 XPathSelector(HashMap<String, serde_json::Value>),
279 DataPositionSelector(HashMap<String, serde_json::Value>),
280 SvgSelector(HashMap<String, serde_json::Value>),
281}
282
283impl Selector {
284 pub fn new_quote(exact: &str, prefix: &str, suffix: &str) -> Self {
285 Self::TextQuoteSelector(TextQuoteSelector {
286 exact: exact.to_string(),
287 prefix: prefix.to_string(),
288 suffix: suffix.to_string(),
289 })
290 }
291}
292
293#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
301pub struct TextQuoteSelector {
302 pub exact: String,
304 pub prefix: String,
306 pub suffix: String,
308}
309
310#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
319pub struct TextPositionSelector {
320 pub start: u64,
323 pub end: u64,
325}
326
327#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
328#[serde(rename_all = "lowercase")]
329pub enum Sort {
330 Created,
331 Updated,
332 Id,
333 Group,
334 User,
335}
336
337impl Default for Sort {
338 fn default() -> Self {
339 Self::Updated
340 }
341}
342
343#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
344#[serde(rename_all = "lowercase")]
345pub enum Order {
346 Asc,
347 Desc,
348}
349
350impl Default for Order {
351 fn default() -> Self {
352 Self::Desc
353 }
354}
355
356#[cfg_attr(feature = "cli", derive(StructOpt))]
358#[derive(Serialize, Debug, Clone, PartialEq, Builder, Default)]
359#[builder(build_fn(name = "builder"), default)]
360pub struct SearchQuery {
361 #[builder(default = "20")]
365 #[cfg_attr(feature = "cli", structopt(default_value = "20", long))]
366 pub limit: u8,
367 #[cfg_attr(feature = "cli", structopt(default_value = "updated", long, possible_values = & Sort::variants()))]
372 pub sort: Sort,
373 #[serde(skip_serializing_if = "is_default")]
378 #[cfg_attr(feature = "cli", structopt(default_value, long))]
379 #[builder(setter(into))]
380 pub search_after: String,
381 #[cfg_attr(feature = "cli", structopt(default_value = "0", long))]
386 pub offset: usize,
387 #[cfg_attr(feature = "cli", structopt(default_value = "desc", long, possible_values = & Order::variants()))]
392 pub order: Order,
393 #[serde(skip_serializing_if = "is_default")]
398 #[cfg_attr(feature = "cli", structopt(default_value, long))]
399 #[builder(setter(into))]
400 pub uri: String,
401 #[serde(rename = "uri.parts", skip_serializing_if = "is_default")]
405 #[cfg_attr(feature = "cli", structopt(default_value, long))]
406 #[builder(setter(into))]
407 pub uri_parts: String,
408 #[serde(rename = "wildcard_uri", skip_serializing_if = "is_default")]
410 #[cfg_attr(feature = "cli", structopt(default_value, long))]
411 #[builder(setter(into))]
412 pub wildcard_uri: String,
413 #[serde(skip_serializing_if = "is_default")]
415 #[cfg_attr(feature = "cli", structopt(default_value, long))]
416 #[builder(setter(into))]
417 pub user: String,
418 #[serde(skip_serializing_if = "is_default")]
420 #[cfg_attr(feature = "cli", structopt(default_value, long))]
421 #[builder(setter(into))]
422 pub group: String,
423 #[serde(skip_serializing_if = "is_default")]
425 #[cfg_attr(feature = "cli", structopt(default_value, long))]
426 #[builder(setter(into))]
427 pub tag: String,
428 #[serde(skip_serializing_if = "is_default")]
430 #[cfg_attr(feature = "cli", structopt(long))]
431 pub tags: Vec<String>,
432 #[serde(skip_serializing_if = "is_default")]
435 #[cfg_attr(feature = "cli", structopt(default_value, long))]
436 #[builder(setter(into))]
437 pub any: String,
438 #[serde(skip_serializing_if = "is_default")]
440 #[cfg_attr(feature = "cli", structopt(default_value, long))]
441 #[builder(setter(into))]
442 pub quote: String,
443 #[serde(skip_serializing_if = "is_default")]
445 #[cfg_attr(feature = "cli", structopt(default_value, long))]
446 #[builder(setter(into))]
447 pub references: String,
448 #[serde(skip_serializing_if = "is_default")]
450 #[cfg_attr(feature = "cli", structopt(default_value, long))]
451 #[builder(setter(into))]
452 pub text: String,
453}
454
455impl SearchQuery {
456 pub fn builder() -> SearchQueryBuilder {
457 SearchQueryBuilder::default()
458 }
459}
460
461impl SearchQueryBuilder {
462 pub fn build(&self) -> Result<SearchQuery, errors::HypothesisError> {
464 self.builder()
465 .map_err(|e| errors::HypothesisError::BuilderError(e.to_string()))
466 }
467}
468
469#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
470pub struct Permissions {
471 pub read: Vec<String>,
472 pub delete: Vec<String>,
473 pub admin: Vec<String>,
474 pub update: Vec<String>,
475}