wip: many pieces moving, still.
Working on many things, though it's starting to take shape; the configuration ouline and its loading (with figment) is well defined already; working on stabilizing the SQLite connection, and being able to insert a slug.
This commit is contained in:
parent
b0be6e999a
commit
4405448fa2
|
@ -2,6 +2,17 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
|
@ -17,6 +28,15 @@ version = "0.5.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -173,13 +193,16 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "envy"
|
||||
version = "0.4.2"
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
|
@ -190,6 +213,20 @@ dependencies = [
|
|||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "figment"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790b4292c72618abbab50f787a477014fe15634f96291de45672ce46afe122df"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"pear",
|
||||
"serde",
|
||||
"toml",
|
||||
"uncased",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -293,6 +330,18 @@ name = "hashbrown"
|
|||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
|
@ -407,6 +456,12 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inlinable_string"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
|
@ -456,6 +511,16 @@ version = "0.2.121"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb644c388dfaefa18035c12614156d285364769e818893da0dda9030c80ad2ba"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
|
@ -485,7 +550,10 @@ name = "lonk"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"envy",
|
||||
"either",
|
||||
"figment",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"tokio",
|
||||
"validators",
|
||||
"warp",
|
||||
|
@ -670,6 +738,29 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pear"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15e44241c5e4c868e3eaa78b7c1848cadd6344ed4f54d029832d32b415a58702"
|
||||
dependencies = [
|
||||
"inlinable_string",
|
||||
"pear_codegen",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pear_codegen"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
|
@ -728,6 +819,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
|
@ -743,6 +840,19 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2-diagnostics"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
|
@ -844,6 +954,21 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"memchr",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
|
@ -888,6 +1013,9 @@ name = "serde"
|
|||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
|
@ -1123,6 +1251,15 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.1"
|
||||
|
@ -1190,6 +1327,15 @@ version = "1.15.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
|
||||
[[package]]
|
||||
name = "uncased"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
|
@ -1283,6 +1429,12 @@ dependencies = [
|
|||
"enum-ordinalize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
@ -1405,3 +1557,9 @@ name = "windows_x86_64_msvc"
|
|||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
|
|
@ -7,7 +7,10 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
base64 = "0.13.0"
|
||||
envy = "0.4.2"
|
||||
either = "1.6.1"
|
||||
figment = { version = "0.10.6", features = ["toml", "env"] }
|
||||
rusqlite = "0.27.0"
|
||||
serde = { version = "~1.0.136", features = ["derive"] }
|
||||
tokio = { version = "~1.17.0", features = ["full"] }
|
||||
validators = "0.24.1"
|
||||
validators = { version = "~0.24.1", features = ["url-dep"] }
|
||||
warp = "0.3.2"
|
||||
|
|
177
src/main.rs
177
src/main.rs
|
@ -1,12 +1,92 @@
|
|||
use std::{collections::BTreeSet, str::FromStr};
|
||||
|
||||
use figment::{
|
||||
providers::{Format, Toml},
|
||||
Figment,
|
||||
};
|
||||
use rusqlite::Connection;
|
||||
use std::{collections::BTreeSet, str::FromStr, sync::Arc};
|
||||
use validators::prelude::*;
|
||||
use warp::{Filter, Reply};
|
||||
use warp::{hyper::StatusCode, Filter};
|
||||
|
||||
macro_rules! unwrap_or_unwrap_err {
|
||||
($x:expr) => {
|
||||
match $x {
|
||||
Ok(x) => x,
|
||||
Err(y) => y,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod config {
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
use warp::{filters::BoxedFilter, Filter};
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub db_location: PathBuf,
|
||||
pub slug_rules: SlugRules,
|
||||
pub serve_rules: ServeRules,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct SlugRules {
|
||||
pub length: usize,
|
||||
pub chars: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub enum ServeRules {
|
||||
File(PathBuf),
|
||||
Dir(PathBuf),
|
||||
}
|
||||
|
||||
impl ServeRules {
|
||||
pub fn to_filter(&self) -> BoxedFilter<(warp::fs::File,)> {
|
||||
match self {
|
||||
ServeRules::File(file) => warp::fs::file(file.clone()).boxed(),
|
||||
ServeRules::Dir(dir) => warp::fs::dir(dir.clone()).boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SlugRules {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
length: 5,
|
||||
chars: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
|
||||
.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ServeRules {
|
||||
fn default() -> Self {
|
||||
ServeRules::Dir(PathBuf::from_str("/etc/lonk/served").unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
db_location: PathBuf::from_str("/etc/lonk/data.db").unwrap(),
|
||||
slug_rules: Default::default(),
|
||||
serve_rules: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Validator)]
|
||||
#[validator(base64_url(padding(NotAllow)))]
|
||||
struct Base64WithoutPaddingUrl(String);
|
||||
|
||||
#[derive(Validator)]
|
||||
#[validator(domain(ipv4(Allow), local(Allow), at_least_two_labels(Allow), port(Allow)))]
|
||||
struct Url {
|
||||
domain: String,
|
||||
port: Option<u16>,
|
||||
}
|
||||
|
||||
impl FromStr for Base64WithoutPaddingUrl {
|
||||
type Err = <Self as ValidateString>::Error;
|
||||
|
||||
|
@ -15,7 +95,22 @@ impl FromStr for Base64WithoutPaddingUrl {
|
|||
}
|
||||
}
|
||||
|
||||
struct SlugParser {
|
||||
struct SlugDatabase(rusqlite::Connection);
|
||||
|
||||
impl SlugDatabase {
|
||||
fn from_connection(connection: rusqlite::Connection) -> Self {
|
||||
// TODO: Check that the database has the necessary format
|
||||
|
||||
SlugDatabase(connection)
|
||||
}
|
||||
|
||||
fn insert_slug(slug: Slug, url: Url) -> Result<(), ()> {
|
||||
todo!();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct SlugFactory {
|
||||
slug_length: usize,
|
||||
slug_chars: BTreeSet<char>,
|
||||
}
|
||||
|
@ -27,34 +122,76 @@ enum InvalidSlug {
|
|||
BadChar,
|
||||
}
|
||||
|
||||
impl SlugParser {
|
||||
fn slug_from_str(s: &str) -> Result<Slug, InvalidSlug> {
|
||||
impl SlugFactory {
|
||||
fn from_rules(rules: config::SlugRules) -> Self {
|
||||
let mut slug_chars = BTreeSet::<char>::new();
|
||||
slug_chars.extend(rules.chars.chars());
|
||||
|
||||
SlugFactory {
|
||||
slug_length: rules.length,
|
||||
slug_chars,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_str(&self, s: &str) -> Result<Slug, InvalidSlug> {
|
||||
for (i, char) in s.chars().enumerate() {
|
||||
if i >= self.slug_length {
|
||||
return Err(InvalidSlug::TooLong);
|
||||
}
|
||||
|
||||
if !self.slug_chars.contains(&char) {
|
||||
return Err(InvalidSlug::BadChar);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Slug(s.to_string()))
|
||||
}
|
||||
|
||||
fn generate(&self) -> Slug {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
async fn shorten<'s>(b64url: &'s str) -> Result<impl Reply, impl Reply> {
|
||||
let url = base64::decode_config(b64url, base64::URL_SAFE_NO_PAD).map_err(|_| {
|
||||
warp::reply::with_status(warp::reply(), warp::http::StatusCode::BAD_REQUEST)
|
||||
})?;
|
||||
todo!();
|
||||
Ok(warp::reply())
|
||||
}
|
||||
|
||||
macro_rules! unwrap_and_err {
|
||||
($x: ident) => {
|
||||
|
||||
fn shorten<'s>(slug_factory: &SlugFactory, db: SlugDatabase, b64url: &'s str) -> Result<StatusCode, StatusCode> {
|
||||
let url = {
|
||||
let raw = base64::decode_config(b64url, base64::URL_SAFE_NO_PAD)
|
||||
.map_err(|_| warp::http::StatusCode::BAD_REQUEST)?;
|
||||
let url_str = std::str::from_utf8(&raw).map_err(|_| warp::http::StatusCode::BAD_REQUEST)?;
|
||||
Url::parse_str(url_str).map_err(|_| warp::http::StatusCode::BAD_REQUEST)?
|
||||
};
|
||||
|
||||
let new_slug = slug_factory.generate();
|
||||
|
||||
Ok(warp::http::StatusCode::OK)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Read configuration
|
||||
let config_file = std::env::var("LONK_CONFIG").unwrap_or("lonk.toml".to_string());
|
||||
let config: config::Config = Figment::new()
|
||||
.merge(Toml::file(&config_file))
|
||||
.extract()
|
||||
.expect("Could not parse configuration file.");
|
||||
|
||||
// Create slug factory
|
||||
let slug_factory = Arc::new(SlugFactory::from_rules(config.slug_rules));
|
||||
|
||||
// Initialize database
|
||||
let db = Connection::open(config.db_location);
|
||||
|
||||
// GET /
|
||||
let homepage = warp::path::end().and(warp::fs::file("index.html"));
|
||||
let homepage = warp::path::end().and(config.serve_rules.to_filter());
|
||||
|
||||
// GET /shorten/:Base64WithoutPaddingUrl
|
||||
let shorten = warp::path!("shorten" / Base64WithoutPaddingUrl)
|
||||
.map(|link: Base64WithoutPaddingUrl| shorten(&link.0));
|
||||
let shorten = warp::path!("shorten" / Base64WithoutPaddingUrl).map({
|
||||
move |link: Base64WithoutPaddingUrl| {
|
||||
warp::reply::with_status(
|
||||
warp::reply(),
|
||||
unwrap_or_unwrap_err!(shorten(&slug_factory, &link.0)),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
// GET /l/:Slug
|
||||
let link = warp::path("l")
|
||||
|
|
Loading…
Reference in New Issue