1use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
6use rusqlite::Result as RusqliteResult;
7use serde::ser::{Serialize, Serializer};
8use std::fmt;
9
10mod visit_transition_set;
11pub use visit_transition_set::VisitTransitionSet;
12
13#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
14#[error("Invalid visit type")]
15pub struct InvalidVisitType;
16
17#[repr(u8)]
21#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
22pub enum VisitType {
23 Link = 1,
25
26 Typed = 2,
29
30 Bookmark = 3,
32 Embed = 4,
39
40 RedirectPermanent = 5,
42
43 RedirectTemporary = 6,
45
46 Download = 7,
48
49 FramedLink = 8,
51
52 Reload = 9,
54
55 UpdatePlace = 10,
57}
58
59impl ToSql for VisitType {
60 fn to_sql(&self) -> RusqliteResult<ToSqlOutput<'_>> {
61 Ok(ToSqlOutput::from(*self as u8))
62 }
63}
64
65impl VisitType {
66 pub fn from_primitive(p: u8) -> Option<Self> {
67 match p {
68 1 => Some(VisitType::Link),
69 2 => Some(VisitType::Typed),
70 3 => Some(VisitType::Bookmark),
71 4 => Some(VisitType::Embed),
72 5 => Some(VisitType::RedirectPermanent),
73 6 => Some(VisitType::RedirectTemporary),
74 7 => Some(VisitType::Download),
75 8 => Some(VisitType::FramedLink),
76 9 => Some(VisitType::Reload),
77 10 => Some(VisitType::UpdatePlace),
78 _ => None,
79 }
80 }
81}
82
83impl TryFrom<u8> for VisitType {
84 type Error = InvalidVisitType;
85 fn try_from(p: u8) -> Result<Self, Self::Error> {
86 VisitType::from_primitive(p).ok_or(InvalidVisitType)
87 }
88}
89
90struct VisitTransitionSerdeVisitor;
91
92impl serde::de::Visitor<'_> for VisitTransitionSerdeVisitor {
93 type Value = VisitType;
94
95 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
96 formatter.write_str("positive integer representing VisitType")
97 }
98
99 fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<VisitType, E> {
100 if value > u64::from(u8::MAX) {
101 return Err(E::custom(format!("value out of u8 range: {}", value)));
104 }
105 VisitType::from_primitive(value as u8)
106 .ok_or_else(|| E::custom(format!("unknown VisitType value: {}", value)))
107 }
108}
109
110impl serde::Serialize for VisitType {
111 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
112 serializer.serialize_u64(*self as u64)
113 }
114}
115
116impl<'de> serde::Deserialize<'de> for VisitType {
117 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
118 deserializer.deserialize_u64(VisitTransitionSerdeVisitor)
119 }
120}
121
122#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
124#[repr(u8)]
125pub enum BookmarkType {
126 Bookmark = 1, Folder = 2, Separator = 3, }
132
133impl FromSql for BookmarkType {
134 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
135 let v = value.as_i64()?;
136 if v < 0 || v > i64::from(u8::MAX) {
137 return Err(FromSqlError::OutOfRange(v));
138 }
139 BookmarkType::from_u8(v as u8).ok_or(FromSqlError::OutOfRange(v))
140 }
141}
142
143impl BookmarkType {
144 #[inline]
145 pub fn from_u8(v: u8) -> Option<Self> {
146 match v {
147 1 => Some(BookmarkType::Bookmark),
148 2 => Some(BookmarkType::Folder),
149 3 => Some(BookmarkType::Separator),
150 _ => None,
151 }
152 }
153
154 pub fn from_u8_with_valid_url<F: Fn() -> bool>(v: u8, has_valid_url: F) -> Self {
155 match BookmarkType::from_u8(v) {
156 Some(BookmarkType::Bookmark) | None => {
157 if has_valid_url() {
158 BookmarkType::Bookmark
161 } else {
162 BookmarkType::Folder
163 }
164 }
165 Some(t) => t,
166 }
167 }
168}
169
170impl ToSql for BookmarkType {
171 fn to_sql(&self) -> RusqliteResult<ToSqlOutput<'_>> {
172 Ok(ToSqlOutput::from(*self as u8))
173 }
174}
175
176impl Serialize for BookmarkType {
177 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
178 where
179 S: Serializer,
180 {
181 serializer.serialize_u8(*self as u8)
182 }
183}
184
185#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
201#[repr(u8)]
202pub enum SyncStatus {
203 Unknown = 0,
204 New = 1,
205 Normal = 2,
206}
207
208impl SyncStatus {
209 #[inline]
210 pub fn from_u8(v: u8) -> Self {
211 match v {
212 1 => SyncStatus::New,
213 2 => SyncStatus::Normal,
214 _ => SyncStatus::Unknown,
215 }
216 }
217}
218
219impl FromSql for SyncStatus {
220 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
221 let v = value.as_i64()?;
222 if v < 0 || v > i64::from(u8::MAX) {
223 return Err(FromSqlError::OutOfRange(v));
224 }
225 Ok(SyncStatus::from_u8(v as u8))
226 }
227}
228
229impl ToSql for SyncStatus {
230 fn to_sql(&self) -> RusqliteResult<ToSqlOutput<'_>> {
231 Ok(ToSqlOutput::from(*self as u8))
232 }
233}
234
235pub type UnknownFields = serde_json::Map<String, serde_json::Value>;
238
239pub(crate) fn serialize_unknown_fields(
240 unknown_fields: &UnknownFields,
241) -> crate::Result<Option<String>> {
242 if unknown_fields.is_empty() {
243 Ok(None)
244 } else {
245 Ok(Some(serde_json::to_string(unknown_fields)?))
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn test_primitive() {
255 assert_eq!(Some(VisitType::Link), VisitType::from_primitive(1));
256 assert_eq!(None, VisitType::from_primitive(99));
257 }
258}