use std::collections::HashMap;
use chrono::{DateTime, Utc};
#[cfg(feature = "cli")]
use clap::Parser;
#[cfg(feature = "cli")]
use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use crate::{errors, is_default, UserAccountID};
#[cfg_attr(feature = "cli", derive(Parser))]
#[cfg_attr(
feature = "cli",
clap(
about = "Create an annotation",
long_about = "Create and upload an annotation to your Hypothesis"
)
)]
#[derive(Serialize, Debug, Default, Clone, Builder, PartialEq)]
#[builder(default, build_fn(name = "builder"))]
pub struct InputAnnotation {
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = ""))]
#[builder(setter(into))]
pub uri: String,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub text: String,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(long))]
#[builder(setter(strip_option), default)]
pub tags: Option<Vec<String>>,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(skip))]
#[builder(setter(strip_option), default)]
pub document: Option<Document>,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub group: String,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(skip))]
pub target: Target,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(long))]
pub references: Vec<String>,
}
impl InputAnnotation {
pub fn builder() -> InputAnnotationBuilder {
InputAnnotationBuilder::default()
}
}
impl InputAnnotationBuilder {
pub fn build(&self) -> Result<InputAnnotation, errors::HypothesisError> {
self.builder()
.map_err(|e| errors::HypothesisError::BuilderError(e.to_string()))
}
}
impl Annotation {
pub fn update(&mut self, annotation: InputAnnotation) {
if !annotation.uri.is_empty() {
self.uri = annotation.uri;
}
if !annotation.text.is_empty() {
self.text = annotation.text;
}
if let Some(tags) = annotation.tags {
self.tags = tags;
}
if !annotation.group.is_empty() {
self.group = annotation.group;
}
if annotation.references.is_empty() {
self.references = annotation.references;
}
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Builder)]
#[builder(build_fn(name = "builder"))]
pub struct Document {
#[serde(skip_serializing_if = "is_default", default)]
pub title: Vec<String>,
#[serde(skip_serializing_if = "is_default", default)]
#[builder(setter(strip_option), default)]
pub dc: Option<Dc>,
#[serde(skip_serializing_if = "is_default", default)]
#[builder(setter(strip_option), default)]
pub highwire: Option<HighWire>,
#[serde(skip_serializing_if = "is_default", default)]
pub link: Vec<Link>,
}
impl Document {
pub fn builder() -> DocumentBuilder {
DocumentBuilder::default()
}
}
impl DocumentBuilder {
pub fn build(&self) -> Result<Document, errors::HypothesisError> {
self.builder()
.map_err(|e| errors::HypothesisError::BuilderError(e.to_string()))
}
}
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct HighWire {
#[serde(skip_serializing_if = "is_default", default)]
pub doi: Vec<String>,
#[serde(skip_serializing_if = "is_default", default)]
pub pdf_url: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Link {
pub href: String,
#[serde(skip_serializing_if = "is_default", rename = "type", default)]
pub link_type: String,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Dc {
#[serde(skip_serializing_if = "is_default", default)]
pub identifier: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Annotation {
pub id: String,
pub created: DateTime<Utc>,
pub updated: DateTime<Utc>,
pub user: UserAccountID,
pub uri: String,
pub text: String,
pub tags: Vec<String>,
pub group: String,
pub permissions: Permissions,
pub target: Vec<Target>,
pub links: HashMap<String, String>,
pub hidden: bool,
pub flagged: bool,
#[serde(default)]
pub document: Option<Document>,
#[serde(default)]
pub references: Vec<String>,
#[serde(default)]
pub user_info: Option<UserInfo>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct UserInfo {
pub display_name: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Builder)]
#[builder(build_fn(name = "builder"))]
pub struct Target {
#[serde(skip_serializing_if = "is_default")]
#[builder(setter(into))]
pub source: String,
#[serde(default, skip_serializing_if = "is_default")]
pub selector: Vec<Selector>,
}
impl Target {
pub fn builder() -> TargetBuilder {
TargetBuilder::default()
}
}
impl TargetBuilder {
pub fn build(&self) -> Result<Target, errors::HypothesisError> {
self.builder()
.map_err(|e| errors::HypothesisError::BuilderError(e.to_string()))
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "type")]
pub enum Selector {
TextQuoteSelector(TextQuoteSelector),
TextPositionSelector(HashMap<String, serde_json::Value>),
RangeSelector(HashMap<String, serde_json::Value>),
FragmentSelector(HashMap<String, serde_json::Value>),
CssSelector(HashMap<String, serde_json::Value>),
XPathSelector(HashMap<String, serde_json::Value>),
DataPositionSelector(HashMap<String, serde_json::Value>),
SvgSelector(HashMap<String, serde_json::Value>),
PageSelector(HashMap<String, serde_json::Value>),
EPUBContentSelector(HashMap<String, serde_json::Value>),
MediaTimeSelector(HashMap<String, serde_json::Value>),
}
impl Selector {
pub fn new_quote(exact: &str, prefix: &str, suffix: &str) -> Self {
Self::TextQuoteSelector(TextQuoteSelector {
exact: exact.to_string(),
prefix: prefix.to_string(),
suffix: suffix.to_string(),
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct TextQuoteSelector {
pub exact: String,
pub prefix: String,
pub suffix: String,
}
#[cfg_attr(feature = "cli", derive(ValueEnum))]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Sort {
Created,
Updated,
Id,
Group,
User,
}
impl Default for Sort {
fn default() -> Self {
Self::Updated
}
}
#[cfg_attr(feature = "cli", derive(ValueEnum))]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Order {
Asc,
Desc,
}
impl Default for Order {
fn default() -> Self {
Self::Desc
}
}
#[cfg_attr(feature = "cli", derive(Parser))]
#[derive(Serialize, Debug, Clone, PartialEq, Builder, Default)]
#[builder(build_fn(name = "builder"), default)]
pub struct SearchQuery {
#[builder(default = "20")]
#[cfg_attr(feature = "cli", clap(default_value = "20", long))]
pub limit: u8,
#[cfg_attr(feature = "cli", clap(default_value = "updated", long, value_parser = clap::builder::EnumValueParser::<Sort>::new()))]
pub sort: Sort,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub search_after: String,
#[cfg_attr(feature = "cli", clap(default_value = "0", long))]
pub offset: usize,
#[cfg_attr(feature = "cli", clap(default_value = "desc", long, value_parser = clap::builder::EnumValueParser::<Order>::new()))]
pub order: Order,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub uri: String,
#[serde(rename = "uri.parts", skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub uri_parts: String,
#[serde(rename = "wildcard_uri", skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub wildcard_uri: String,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub user: String,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(long))]
#[builder(setter(into))]
pub group: Vec<String>,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub tag: String,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(long))]
pub tags: Vec<String>,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub any: String,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub quote: String,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub references: String,
#[serde(skip_serializing_if = "is_default")]
#[cfg_attr(feature = "cli", clap(default_value = "", long))]
#[builder(setter(into))]
pub text: String,
}
impl SearchQuery {
pub fn builder() -> SearchQueryBuilder {
SearchQueryBuilder::default()
}
}
impl SearchQueryBuilder {
pub fn build(&self) -> Result<SearchQuery, errors::HypothesisError> {
self.builder()
.map_err(|e| errors::HypothesisError::BuilderError(e.to_string()))
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Permissions {
pub read: Vec<String>,
pub delete: Vec<String>,
pub admin: Vec<String>,
pub update: Vec<String>,
}