1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use super::common::ID;
use crate::utils::serde::parse_stringint;
/// Type alias for clarity.
pub type Annotations = Vec<Annotation>;
/// Represents an annotation as returned from the API.
///
/// Annotations are in Annotatorjs format. <https://annotatorjs.org/>
/// See <http://docs.annotatorjs.org/en/v1.2.x/annotation-format.html> for documentation on the
/// format.
///
#[derive(Deserialize, Serialize, Debug)]
pub struct Annotation {
/// The unique integral id of the annotation.
pub id: ID,
/// A schema version to presumably support updates in the future. Currently all annotations
/// appear to be `v1.0`. Hopefully this isn't going to get breaking changes any time soon.
pub annotator_schema_version: String,
/// When the annotation was created on the server.
pub created_at: DateTime<Utc>,
/// The quoted (or highlighted) text from the entry.
pub quote: Option<String>,
/// A list of ranges from the entry that the annotation covers. Most annotations cover a single
/// range.
pub ranges: Vec<Range>,
/// The content of the annotation - any text the user added to annotate the entry.
pub text: String,
/// Timestamp of when the annotation was last updated. This is independent of the associated
/// entry.
pub updated_at: DateTime<Utc>,
/// Possibly part of wallabag planning on supporting sharing between users. Currently this
/// field is always `None`.
pub user: Option<String>,
}
/// This is implemented so that an Annotation can be used interchangeably with an ID for some
/// client methods. For convenience.
impl From<Annotation> for ID {
fn from(ann: Annotation) -> Self {
ann.id
}
}
/// This is implemented so that an &Annotation can be used interchangeably with an ID
/// for some client methods. For convenience.
impl From<&Annotation> for ID {
fn from(ann: &Annotation) -> Self {
ann.id
}
}
/// Intermediary struct for deserializing a list of annotations.
#[derive(Deserialize, Debug)]
pub(crate) struct AnnotationRows {
pub rows: Annotations,
}
/// Represents an annotation to be created (hence no ID yet).
/// Fields are defined as in a full annotation.
#[derive(Serialize, Debug)]
pub struct NewAnnotation {
/// TODO, XXX: quote must not be an empty string.
pub quote: String,
pub ranges: Vec<Range>,
pub text: String,
}
/// Range as used in an `Annotation`. Shows where the annotation is in the
/// content. Part of Annotationjs annotation format. I quote from their docs for the field
/// descriptions.
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Range {
/// (relative) XPath to start element.
pub start: Option<String>,
/// (relative) XPath to end element.
pub end: Option<String>,
/// Character offset within start element. Note: these offset values have been observed as
/// literal strings and integers. Grrr loosely typed languages with coercion...
#[serde(deserialize_with = "parse_stringint")]
pub start_offset: u32,
/// Character offset within end element.
#[serde(deserialize_with = "parse_stringint")]
pub end_offset: u32,
}