use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use rusqlite::Result as RusqliteResult;
use serde::ser::{Serialize, Serializer};
use std::fmt;
mod visit_transition_set;
pub use visit_transition_set::VisitTransitionSet;
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[error("Invalid visit type")]
pub struct InvalidVisitType;
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum VisitType {
Link = 1,
Typed = 2,
Bookmark = 3,
Embed = 4,
RedirectPermanent = 5,
RedirectTemporary = 6,
Download = 7,
FramedLink = 8,
Reload = 9,
UpdatePlace = 10,
}
impl ToSql for VisitType {
fn to_sql(&self) -> RusqliteResult<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(*self as u8))
}
}
impl VisitType {
pub fn from_primitive(p: u8) -> Option<Self> {
match p {
1 => Some(VisitType::Link),
2 => Some(VisitType::Typed),
3 => Some(VisitType::Bookmark),
4 => Some(VisitType::Embed),
5 => Some(VisitType::RedirectPermanent),
6 => Some(VisitType::RedirectTemporary),
7 => Some(VisitType::Download),
8 => Some(VisitType::FramedLink),
9 => Some(VisitType::Reload),
10 => Some(VisitType::UpdatePlace),
_ => None,
}
}
}
impl TryFrom<u8> for VisitType {
type Error = InvalidVisitType;
fn try_from(p: u8) -> Result<Self, Self::Error> {
VisitType::from_primitive(p).ok_or(InvalidVisitType)
}
}
struct VisitTransitionSerdeVisitor;
impl serde::de::Visitor<'_> for VisitTransitionSerdeVisitor {
type Value = VisitType;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("positive integer representing VisitType")
}
fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<VisitType, E> {
if value > u64::from(u8::MAX) {
return Err(E::custom(format!("value out of u8 range: {}", value)));
}
VisitType::from_primitive(value as u8)
.ok_or_else(|| E::custom(format!("unknown VisitType value: {}", value)))
}
}
impl serde::Serialize for VisitType {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_u64(*self as u64)
}
}
impl<'de> serde::Deserialize<'de> for VisitType {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_u64(VisitTransitionSerdeVisitor)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(u8)]
pub enum BookmarkType {
Bookmark = 1, Folder = 2, Separator = 3, }
impl FromSql for BookmarkType {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
let v = value.as_i64()?;
if v < 0 || v > i64::from(u8::MAX) {
return Err(FromSqlError::OutOfRange(v));
}
BookmarkType::from_u8(v as u8).ok_or(FromSqlError::OutOfRange(v))
}
}
impl BookmarkType {
#[inline]
pub fn from_u8(v: u8) -> Option<Self> {
match v {
1 => Some(BookmarkType::Bookmark),
2 => Some(BookmarkType::Folder),
3 => Some(BookmarkType::Separator),
_ => None,
}
}
pub fn from_u8_with_valid_url<F: Fn() -> bool>(v: u8, has_valid_url: F) -> Self {
match BookmarkType::from_u8(v) {
Some(BookmarkType::Bookmark) | None => {
if has_valid_url() {
BookmarkType::Bookmark
} else {
BookmarkType::Folder
}
}
Some(t) => t,
}
}
}
impl ToSql for BookmarkType {
fn to_sql(&self) -> RusqliteResult<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(*self as u8))
}
}
impl Serialize for BookmarkType {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u8(*self as u8)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(u8)]
pub enum SyncStatus {
Unknown = 0,
New = 1,
Normal = 2,
}
impl SyncStatus {
#[inline]
pub fn from_u8(v: u8) -> Self {
match v {
1 => SyncStatus::New,
2 => SyncStatus::Normal,
_ => SyncStatus::Unknown,
}
}
}
impl FromSql for SyncStatus {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
let v = value.as_i64()?;
if v < 0 || v > i64::from(u8::MAX) {
return Err(FromSqlError::OutOfRange(v));
}
Ok(SyncStatus::from_u8(v as u8))
}
}
impl ToSql for SyncStatus {
fn to_sql(&self) -> RusqliteResult<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(*self as u8))
}
}
pub type UnknownFields = serde_json::Map<String, serde_json::Value>;
pub(crate) fn serialize_unknown_fields(
unknown_fields: &UnknownFields,
) -> crate::Result<Option<String>> {
if unknown_fields.is_empty() {
Ok(None)
} else {
Ok(Some(serde_json::to_string(unknown_fields)?))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_primitive() {
assert_eq!(Some(VisitType::Link), VisitType::from_primitive(1));
assert_eq!(None, VisitType::from_primitive(99));
}
}