wallabag_api/types/
annotations.rs

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