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}