links/store/backend.rs
1//! This module contains a trait and its implementations for a unified
2//! redirect, vanity path, and statistics store used as links' backend store.
3//!
4//! This store can be something simple like in-memory hashmaps, an interface to
5//! something more complex and feature-rich like Redis, or anything in between.
6//! The aim of this [`StoreBackend`] trait is to make it easy to swap between
7//! different storage backends, and to make developing them fast. For details
8//! about configuring each store backend, see that backend's documentation.
9
10use core::fmt::Debug;
11use std::collections::HashMap;
12
13use anyhow::Result;
14use async_trait::async_trait;
15use links_id::Id;
16use links_normalized::{Link, Normalized};
17
18use crate::{
19 stats::{Statistic, StatisticDescription, StatisticValue},
20 store::BackendType,
21};
22
23/// The redirect, vanity path, and statistics store trait used by links.
24#[async_trait]
25pub trait StoreBackend: Debug + Send + Sync {
26 /// Get this implementation's backend store type. This is used in
27 /// e.g. the configuration.
28 fn store_type() -> BackendType
29 where
30 Self: Sized;
31
32 /// Get this implementation's backend store type. This can be used on trait
33 /// objects, but is otherwise equivalent to calling `Self::store_type()`.
34 fn get_store_type(&self) -> BackendType;
35
36 /// Create a new instance of this `StoreBackend`. Configuration is provided
37 /// as a collection of `pico-args` arguments beginning with `--store-`. For
38 /// details about configuring each store backend, see that backend's
39 /// documentation.
40 async fn new(config: &HashMap<String, String>) -> Result<Self>
41 where
42 Self: Sized;
43
44 /// Get a redirect. Returns the full `to` link corresponding to the `from`
45 /// links ID. A link not existing is not an error, if no matching link is
46 /// found, `Ok(None)` is returned.
47 ///
48 /// # Error
49 /// An error is only returned if something actually fails; if we don't know
50 /// if a link exists or not, or what it is. A link not existing is not
51 /// considered an error.
52 async fn get_redirect(&self, from: Id) -> Result<Option<Link>>;
53
54 /// Set a redirect. `from` is the ID of the link, while `to` is the full
55 /// destination link. If a mapping with this ID already exists, it must be
56 /// changed to the new one, returning the old one.
57 ///
58 /// # Storage Guarantees
59 /// If an `Ok` is returned, the new value was definitely set / processed /
60 /// saved, and will be available on next request.
61 /// If an `Err` is returned, the value must not have been set / modified,
62 /// insofar as that is possible to determine from the backend.
63 async fn set_redirect(&self, from: Id, to: Link) -> Result<Option<Link>>;
64
65 /// Remove a redirect. `from` is the ID of the links link to be removed.
66 /// Returns the old value of the mapping or `None` if there was no such
67 /// mapping.
68 ///
69 /// # Storage Guarantees
70 /// If an `Ok` is returned, the new value was definitely removed /
71 /// processed / saved, and will be unavailable on next request.
72 /// If an `Err` is returned, the value must not have been removed /
73 /// modified, insofar as that is possible to determine from the backend.
74 async fn rem_redirect(&self, from: Id) -> Result<Option<Link>>;
75
76 /// Get a vanity path's ID. Returns the ID of the `to` link corresponding
77 /// to the `from` vanity path. An ID not existing is not an error, if no
78 /// matching ID is found, `None` is returned.
79 ///
80 /// # Error
81 /// An error is only returned if something actually fails; if we don't know
82 /// if a link exists or not, or what it is. A link not existing is not
83 /// considered an error.
84 async fn get_vanity(&self, from: Normalized) -> Result<Option<Id>>;
85
86 /// Set a vanity path for an ID. `from` is the vanity path of the links ID,
87 /// while `to` is the ID itself. If a vanity link with this path already
88 /// exists, it must be changed to the new one, returning the old one.
89 ///
90 /// # Storage Guarantees
91 /// If an `Ok` is returned, the new value was definitely set / processed /
92 /// saved, and will be available on next request.
93 /// If an `Err` is returned, the value must not have been set / modified,
94 /// insofar as that is possible to determine from the backend.
95 async fn set_vanity(&self, from: Normalized, to: Id) -> Result<Option<Id>>;
96
97 /// Remove a vanity path. `from` is the vanity path to be removed. Returns
98 /// the old value of the mapping or `None` if there was no such mapping.
99 ///
100 /// # Storage Guarantees
101 /// If an `Ok` is returned, the new value was definitely removed /
102 /// processed / saved, and will be unavailable on next request.
103 /// If an `Err` is returned, the value must not have been removed /
104 /// modified, insofar as that is possible to determine from the backend.
105 async fn rem_vanity(&self, from: Normalized) -> Result<Option<Id>>;
106
107 /// Get statistics' values by their description. Returns all matching
108 /// [`Statistic`]s and their values for the provided
109 /// [`StatisticDescription`]. Statistics not having been collected is not an
110 /// error, if no matching statistics are found, an empty [`Vec`] is
111 /// returned.
112 ///
113 /// By default this function returns an empty [`Vec`]
114 ///
115 /// # Error
116 /// An error is only returned if something fails when it should have worked.
117 /// A statistic not existing or the store not supporting statistics is not
118 /// considered an error.
119 async fn get_statistics(
120 &self,
121 _description: StatisticDescription,
122 ) -> Result<Vec<(Statistic, StatisticValue)>> {
123 Ok(Vec::new())
124 }
125
126 /// Increment a statistic's count. The provided [`Statistic`]'s value is
127 /// incremented by 1. Returns the new value of the statistic after the
128 /// increment, or `None` if the statistic wasn't recorded or its new value
129 /// is not known.
130 ///
131 /// By default this function does nothing and returns `Ok(None)`
132 ///
133 /// # Error
134 /// An error is only returned if something fails when it should have worked.
135 /// A statistic not being recorded (immediately or ever) is not considered
136 /// and error.
137 async fn incr_statistic(&self, _statistic: Statistic) -> Result<Option<StatisticValue>> {
138 Ok(None)
139 }
140
141 /// Remove statistics by their description. Deletes all [`Statistic`]s that
142 /// match the provided [`StatisticDescription`] and returns their values
143 /// before they were deleted, if they're available. A statistic not having
144 /// been collected is not an error, if no matching statistics are found, an
145 /// empty [`Vec`] is returned.
146 ///
147 /// By default this function does nothing and returns an empty [`Vec`]
148 ///
149 /// # Error
150 /// An error is only returned if something fails when it should have worked.
151 /// A statistic not existing or the store not supporting statistics is not
152 /// considered an error.
153 async fn rem_statistics(
154 &self,
155 _description: StatisticDescription,
156 ) -> Result<Vec<(Statistic, StatisticValue)>> {
157 Ok(Vec::new())
158 }
159}