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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use crate::utils::serde::parse_intbool;

use super::annotations::Annotations;
use super::common::ID;
use super::tags::Tags;

/// type alias: a list of entries as returned from some endpoints
pub type Entries = Vec<Entry>;

/// A struct representing an entry from wallabag (a full saved article including
/// all annotations and tags; annotations and tags do not need to be requested
/// separately).
///
/// Most fields are controlled by the server. When creating an entry, the server will send a
/// request to the given url and use the response to populate many of the fields. This response is
/// what `headers`, `http_status`, `mimetype`, etc. are referring to.
#[derive(Deserialize, Serialize, Debug)]
pub struct Entry {
    /// Annotation objects for this entry.
    pub annotations: Option<Annotations>,

    /// Content. Should be HTML if present.
    pub content: Option<String>,

    /// The timestamp of when the entry was created on the server.
    pub created_at: DateTime<Utc>,

    /// The resolved domain name of the url. Could be None if the server couldn't resolve the url.
    pub domain_name: Option<String>,

    /// A map of header name -> header value. These appear to be headers from the original source
    /// url.
    pub headers: Option<HashMap<String, String>>,

    /// I'm guessing this is the status the server got when retrieving the content from the url.
    pub http_status: Option<String>,

    /// ID of the entry. Should be an integer. Should also be unique, so can use this directly as
    /// the local id if storing the entry in a DB.
    pub id: ID,

    /// The archived (or read) status of the entry. These boolean options are sometimes represented
    /// as 0 or 1 from the API, which makes parsing in a strongly typed language annoying.
    #[serde(deserialize_with = "parse_intbool")]
    pub is_archived: bool,

    /// The public shared status of the entry. If this is true, then there should be a public link
    /// to this exact entry on the server. The link will be based around the value of the `uid`
    /// field and (TODO: confirm if this can be relied on) formatted as BASE_URL/share/UID.
    #[serde(deserialize_with = "parse_intbool")]
    pub is_public: bool,

    /// The starred status of the entry.
    #[serde(deserialize_with = "parse_intbool")]
    pub is_starred: bool,

    /// The language of the entry - probably generated by the server from inspecting the response.
    pub language: Option<String>,

    /// The mimetype of the entry - probably generated by the server from inspecting the response.
    /// Not sure about the support status for other mimetypes. Observed behaviour suggests that the
    /// server converts everything to HTML - eg. a text/plain mimetype content will be plain text
    /// surrounded by `<pre>` tags.
    pub mimetype: Option<String>,

    /// Supposedly the original url given will be stored here. If a shortened link is submitted to
    /// the server, the short link will be here, but the resolved link will be in URL. Observed
    /// behaviour is that this field is never set.
    pub origin_url: Option<String>,

    /// Optional url for an image related to the entry. Eg. for displaying as a background image to
    /// the entry tile.
    pub preview_picture: Option<String>,

    /// Data about when the entry was published (scraped from the original web page).
    pub published_at: Option<DateTime<Utc>>,

    /// Data about who published the entry (scraped from the original web page).
    pub published_by: Option<Vec<Option<String>>>,

    /// Estimated reading time in minutes. Generated by the server, probably based off your set
    /// reading speed or a default.
    pub reading_time: u32,

    /// Timestamp of when the entry was starred, if it is starred. Unstarring an entry sets this to
    /// None.
    pub starred_at: Option<DateTime<Utc>>,

    /// A list of tag objects associated with this entry.
    pub tags: Tags,

    /// An optional title for the entry.
    pub title: Option<String>,

    /// This will be only set by the server as a unique id to identify the entry if it has been
    /// shared. For example if you share via public link on framabag and the uid is FOO, then the
    /// public url will be framabag.org/share/FOO
    pub uid: Option<String>,

    /// Timestamp when the entry was last updated. This is bumped for any change to any field
    /// attached to the entry except for annotations.
    ///
    /// TODO: check if entry updates if a tag is globally edited (eg. renamed)
    pub updated_at: DateTime<Utc>,

    /// Resolved url of the entry. If the origin_url redirected to a different url (eg. via a
    /// shortened link), the final url will be stored here.
    pub url: Option<String>,

    /// Email of the user who owns this entry. Currently `user_*` fields are redundant since you
    /// can only access entries that belong to you. Entry sharing between users is planned for the
    /// future so this may become relevant soon.
    pub user_email: String,

    /// ID of the user who owns this entry.
    pub user_id: ID,

    /// username of the user who owns this entry.
    pub user_name: String,
}

/// A struct representing a deleted entry from wallabag (a full saved article including
/// annotations and tags). The only difference from the full entry is that this
/// doesn't have an id. Only used internally because a full entry gets
/// reconstituted before being returned to the client.
#[derive(Deserialize, Debug)]
pub(crate) struct DeletedEntry {
    pub annotations: Option<Annotations>,
    pub content: Option<String>,
    pub created_at: DateTime<Utc>,
    pub domain_name: Option<String>,
    pub headers: Option<HashMap<String, String>>,
    pub http_status: Option<String>,

    #[serde(deserialize_with = "parse_intbool")]
    pub is_archived: bool,

    #[serde(deserialize_with = "parse_intbool")]
    pub is_public: bool,

    #[serde(deserialize_with = "parse_intbool")]
    pub is_starred: bool,
    pub language: Option<String>,
    pub mimetype: Option<String>,
    pub origin_url: Option<String>,
    pub preview_picture: Option<String>,
    pub published_at: Option<DateTime<Utc>>,
    pub published_by: Option<Vec<Option<String>>>,
    pub reading_time: u32,
    pub starred_at: Option<DateTime<Utc>>,
    pub tags: Tags,
    pub title: Option<String>,
    pub uid: Option<String>,
    pub updated_at: DateTime<Utc>,
    pub url: Option<String>,
    pub user_email: String,
    pub user_id: ID,
    pub user_name: String,
}

/// This is implemented so that an Entry can be used interchangeably with an ID
/// for some client methods. For convenience.
impl From<Entry> for ID {
    fn from(entry: Entry) -> Self {
        entry.id
    }
}

/// This is implemented so that an &Entry can be used interchangeably with an ID
/// for some client methods. For convenience.
impl From<&Entry> for ID {
    fn from(entry: &Entry) -> Self {
        entry.id
    }
}

/// Internal struct for retrieving a list of entries from the api when
/// paginated.
#[derive(Deserialize, Debug)]
pub(crate) struct PaginatedEntries {
    pub limit: u32,
    pub page: u32,
    pub pages: u32,
    pub total: u32,
    #[serde(rename = "_embedded")]
    pub embedded: EmbeddedEntries,
}

/// Entries as stored in `PaginatedEntries`.
#[derive(Deserialize, Debug)]
pub(crate) struct EmbeddedEntries {
    pub items: Entries,
}

/// Represents a page of Entries returned. Includes both the payload and metadata about the page.
#[derive(Debug)]
pub struct EntriesPage {
    /// Number of entries returned per page. This is set by the server; useful to know if you're
    /// accepting the server default because this will inform what the server default is.
    pub per_page: u32,

    /// The current page number of results.
    pub current_page: u32,

    /// Total number of pages in the set.
    pub total_pages: u32,

    /// Total number of entries in the query set.
    pub total_entries: u32,

    /// The list of entries returned.
    pub entries: Entries,
}