use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Link {
pub code: String,
pub short_url: String,
pub domain_hostname: Option<String>,
pub target_url: String,
pub project_id: String,
pub expires_at: Option<i64>,
pub is_active: bool,
pub seo_title: Option<String>,
pub seo_description: Option<String>,
pub seo_image_url: Option<String>,
pub seo_canonical_url: Option<String>,
#[serde(default)]
pub seo_noindex: bool,
pub seo_updated_at: Option<i64>,
pub created_at: i64,
pub updated_at: i64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct CreateLinkInput {
pub target_url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain_hostname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seo_title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seo_description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seo_image_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seo_canonical_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seo_noindex: Option<bool>,
}
impl CreateLinkInput {
pub fn new(target_url: impl Into<String>) -> Self {
Self {
target_url: target_url.into(),
..Self::default()
}
}
#[must_use]
pub fn with_domain_hostname(mut self, domain_hostname: impl Into<String>) -> Self {
self.domain_hostname = Some(domain_hostname.into());
self
}
#[must_use]
pub fn with_code(mut self, code: impl Into<String>) -> Self {
self.code = Some(code.into());
self
}
#[must_use]
pub fn with_expires_at(mut self, expires_at: i64) -> Self {
self.expires_at = Some(expires_at);
self
}
#[must_use]
pub fn with_seo_title(mut self, seo_title: impl Into<String>) -> Self {
self.seo_title = Some(seo_title.into());
self
}
#[must_use]
pub fn with_seo_description(mut self, seo_description: impl Into<String>) -> Self {
self.seo_description = Some(seo_description.into());
self
}
#[must_use]
pub fn with_seo_image_url(mut self, seo_image_url: impl Into<String>) -> Self {
self.seo_image_url = Some(seo_image_url.into());
self
}
#[must_use]
pub fn with_seo_canonical_url(mut self, seo_canonical_url: impl Into<String>) -> Self {
self.seo_canonical_url = Some(seo_canonical_url.into());
self
}
#[must_use]
pub fn with_seo_noindex(mut self, seo_noindex: bool) -> Self {
self.seo_noindex = Some(seo_noindex);
self
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct UpdateLinkInput {
#[serde(skip_serializing_if = "Option::is_none")]
pub target_url: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<Option<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_active: Option<Option<bool>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seo_title: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seo_description: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seo_image_url: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seo_canonical_url: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seo_noindex: Option<Option<bool>>,
}
impl UpdateLinkInput {
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.target_url.is_none()
&& self.expires_at.is_none()
&& self.is_active.is_none()
&& self.seo_title.is_none()
&& self.seo_description.is_none()
&& self.seo_image_url.is_none()
&& self.seo_canonical_url.is_none()
&& self.seo_noindex.is_none()
}
#[must_use]
pub fn set_target_url(mut self, target_url: impl Into<String>) -> Self {
self.target_url = Some(Some(target_url.into()));
self
}
#[must_use]
pub fn set_expires_at(mut self, expires_at: i64) -> Self {
self.expires_at = Some(Some(expires_at));
self
}
#[must_use]
pub fn clear_expires_at(mut self) -> Self {
self.expires_at = Some(None);
self
}
#[must_use]
pub fn set_is_active(mut self, is_active: bool) -> Self {
self.is_active = Some(Some(is_active));
self
}
#[must_use]
pub fn set_seo_title(mut self, seo_title: impl Into<String>) -> Self {
self.seo_title = Some(Some(seo_title.into()));
self
}
#[must_use]
pub fn clear_seo_title(mut self) -> Self {
self.seo_title = Some(None);
self
}
#[must_use]
pub fn set_seo_description(mut self, seo_description: impl Into<String>) -> Self {
self.seo_description = Some(Some(seo_description.into()));
self
}
#[must_use]
pub fn clear_seo_description(mut self) -> Self {
self.seo_description = Some(None);
self
}
#[must_use]
pub fn set_seo_image_url(mut self, seo_image_url: impl Into<String>) -> Self {
self.seo_image_url = Some(Some(seo_image_url.into()));
self
}
#[must_use]
pub fn clear_seo_image_url(mut self) -> Self {
self.seo_image_url = Some(None);
self
}
#[must_use]
pub fn set_seo_canonical_url(mut self, seo_canonical_url: impl Into<String>) -> Self {
self.seo_canonical_url = Some(Some(seo_canonical_url.into()));
self
}
#[must_use]
pub fn clear_seo_canonical_url(mut self) -> Self {
self.seo_canonical_url = Some(None);
self
}
#[must_use]
pub fn set_seo_noindex(mut self, seo_noindex: bool) -> Self {
self.seo_noindex = Some(Some(seo_noindex));
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ListLinksResult {
pub links: Vec<Link>,
pub next_cursor: Option<i64>,
}
impl ListLinksResult {
pub fn has_more(&self) -> bool {
self.next_cursor.is_some()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatsBreakdown {
#[serde(default)]
pub value: String,
#[serde(default)]
pub clicks: i64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DailyClicksPoint {
pub day: i64,
#[serde(default)]
pub clicks: i64,
#[serde(default)]
pub qr_scans: i64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LinkStats {
#[serde(default)]
pub code: String,
#[serde(default)]
pub days: i64,
#[serde(default)]
pub total_clicks: i64,
#[serde(default)]
pub qr_scans: i64,
#[serde(default)]
pub countries: Vec<StatsBreakdown>,
#[serde(default)]
pub referrers: Vec<StatsBreakdown>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProjectStats {
#[serde(default)]
pub days: i64,
#[serde(default)]
pub total_clicks: i64,
#[serde(default)]
pub qr_scans: i64,
#[serde(default)]
pub daily: Vec<DailyClicksPoint>,
#[serde(default)]
pub countries: Vec<StatsBreakdown>,
#[serde(default)]
pub referrers: Vec<StatsBreakdown>,
#[serde(default)]
pub devices: Vec<StatsBreakdown>,
#[serde(default)]
pub browsers: Vec<StatsBreakdown>,
#[serde(default)]
pub top_codes: Vec<StatsBreakdown>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProjectInfo {
pub id: String,
pub name: String,
pub slug: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct DeleteLinkResult {
#[serde(default)]
pub deleted: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum QrEcc {
#[serde(rename = "L")]
L,
#[serde(rename = "M")]
M,
#[serde(rename = "Q")]
Q,
#[serde(rename = "H")]
H,
}
impl QrEcc {
pub fn as_str(self) -> &'static str {
match self {
QrEcc::L => "L",
QrEcc::M => "M",
QrEcc::Q => "Q",
QrEcc::H => "H",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum QrRefresh {
On,
Value(String),
}
impl QrRefresh {
pub fn from_bool(value: bool) -> Option<Self> {
if value { Some(QrRefresh::On) } else { None }
}
pub fn from_value(value: impl Into<String>) -> Option<Self> {
let s = value.into();
if s.is_empty() {
None
} else {
Some(QrRefresh::Value(s))
}
}
pub fn as_str(&self) -> &str {
match self {
QrRefresh::On => "1",
QrRefresh::Value(s) => s.as_str(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct QrOptions {
pub size: Option<u32>,
pub margin: Option<u32>,
pub ecc: Option<QrEcc>,
pub domain: Option<String>,
pub refresh: Option<QrRefresh>,
}
impl QrOptions {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_size(mut self, size: u32) -> Self {
self.size = Some(size);
self
}
#[must_use]
pub fn with_margin(mut self, margin: u32) -> Self {
self.margin = Some(margin);
self
}
#[must_use]
pub fn with_ecc(mut self, ecc: QrEcc) -> Self {
self.ecc = Some(ecc);
self
}
#[must_use]
pub fn with_domain(mut self, domain: impl Into<String>) -> Self {
self.domain = Some(domain.into());
self
}
#[must_use]
pub fn with_refresh(mut self, refresh: QrRefresh) -> Self {
self.refresh = Some(refresh);
self
}
pub fn is_empty(&self) -> bool {
self.size.is_none()
&& self.margin.is_none()
&& self.ecc.is_none()
&& self.domain.is_none()
&& self.refresh.is_none()
}
pub fn to_query_pairs(&self) -> Vec<(&'static str, String)> {
let mut out: Vec<(&'static str, String)> = Vec::new();
if let Some(size) = self.size {
out.push(("size", size.to_string()));
}
if let Some(margin) = self.margin {
out.push(("margin", margin.to_string()));
}
if let Some(ecc) = self.ecc {
out.push(("ecc", ecc.as_str().to_string()));
}
if let Some(ref domain) = self.domain {
out.push(("domain", domain.clone()));
}
if let Some(ref refresh) = self.refresh {
out.push(("refresh", refresh.as_str().to_string()));
}
out
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ListLinksParams {
pub cursor: Option<i64>,
pub limit: Option<u32>,
}