diff --git a/.gitignore b/.gitignore index ea8c4bf..a2a1b8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +names.json diff --git a/Cargo.lock b/Cargo.lock index 1d6a58a..d7d739a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,55 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "askama" version = "0.12.0" @@ -24,9 +73,6 @@ source = "git+https://github.com/djc/askama.git?rev=b9e51601560398766eac445517fb dependencies = [ "askama_derive", "askama_escape", - "humansize", - "num-traits", - "percent-encoding", ] [[package]] @@ -34,13 +80,11 @@ name = "askama_derive" version = "0.12.1" source = "git+https://github.com/djc/askama.git?rev=b9e51601560398766eac445517fb17c35090a952#b9e51601560398766eac445517fb17c35090a952" dependencies = [ - "basic-toml", "mime", "mime_guess", "nom", "proc-macro2", "quote", - "serde", "syn", ] @@ -118,15 +162,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "basic-toml" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" -dependencies = [ - "serde", -] - [[package]] name = "binascii" version = "0.1.4" @@ -172,6 +207,63 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "cli" +version = "0.1.0" +dependencies = [ + "clap", + "serde", + "serde_json", + "shared", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "cookie" version = "0.17.0" @@ -439,6 +531,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.2.6" @@ -488,15 +586,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - [[package]] name = "hyper" version = "0.14.27" @@ -597,12 +686,6 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" -[[package]] -name = "libm" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" - [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -663,6 +746,9 @@ dependencies = [ "askama_rocket", "rocket", "rust-embed", + "serde", + "serde_json", + "shared", ] [[package]] @@ -747,15 +833,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - [[package]] name = "num_cpus" version = "1.15.0" @@ -1206,6 +1283,14 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shared" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1264,6 +1349,12 @@ dependencies = [ "loom", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "2.0.22" @@ -1533,6 +1624,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index a54a5aa..aa5abe7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[workspace] +members = ["cli", "shared"] + [package] name = "meowy-webring" version = "0.1.0" @@ -11,18 +14,27 @@ default_features = false features = ["json"] [dependencies.rust-embed] -version = "6.7.0" +version = "6" features = ["debug-embed"] +[dependencies.serde] +version = "1.0" + +[dependencies.serde_json] +version = "1.0" + [dependencies.askama_rocket] git = "https://github.com/djc/askama.git" package = "askama_rocket" rev = "b9e51601560398766eac445517fb17c35090a952" +default-features = false [dependencies.askama] git = "https://github.com/djc/askama.git" package = "askama" rev = "b9e51601560398766eac445517fb17c35090a952" version = "0.12" -default-features = true -features = ["with-rocket", "mime", "mime_guess"] +default-features = false + +[dependencies.shared] +path = "./shared" diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 0000000..a79600c --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "cli" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = "1.0" +serde_json = "1.0" + +[dependencies.clap] +version = "4" +features = ["derive"] + +[dependencies.shared] +path = "../shared" + +[[bin]] +name = "meowy-cli" +path = "src/main.rs" diff --git a/cli/src/arguments.rs b/cli/src/arguments.rs new file mode 100644 index 0000000..952da36 --- /dev/null +++ b/cli/src/arguments.rs @@ -0,0 +1,55 @@ +use clap::{arg, command, Args, Parser, Subcommand}; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub(crate) struct Arguments { + #[arg(help = "the path to the names.json file")] + pub(crate) path: String, + #[command(subcommand)] + pub(crate) command: Commands, +} + +#[derive(Subcommand, Debug)] +pub(crate) enum Commands { + #[command(about = "print the current webring sites and their names")] + Print { + #[command(flatten)] + group: PrintGroup, + }, + #[command(about = "add a site to the webring")] + Add { + #[arg( + long, + short, + required = true, + help = "the url of the site you want to add. example: \"example.com\"." + )] + url: String, + #[arg( + long, + short, + required = false, + help = "the personal name of the site. this is not required." + )] + name: Option, + }, + #[command(about = "remove a site from the webring")] + Remove { + #[arg( + long, + short, + required = true, + help = "the url of the site you want to remove." + )] + url: String, + }, +} + +#[derive(Args, Debug)] +#[group(required = false)] +pub struct PrintGroup { + #[arg(long, short, action = clap::ArgAction::SetTrue, conflicts_with = "name", help = "print the url only")] + pub(crate) url: bool, + #[arg(long, short, action = clap::ArgAction::SetTrue, conflicts_with = "url", help = "print the name only")] + pub(crate) name: bool, +} diff --git a/cli/src/commands.rs b/cli/src/commands.rs new file mode 100644 index 0000000..e2cdebb --- /dev/null +++ b/cli/src/commands.rs @@ -0,0 +1,54 @@ +use shared::{ + errors::{Error, ErrorStatus}, + names::Site, +}; + +use crate::arguments::PrintGroup; + +fn read_names(path: &String) -> Result, Error> { + match std::fs::read_to_string(path) { + Ok(names) => { + return shared::names::load_names(names); + } + Err(err) => Err(Error { + status: ErrorStatus::IOError, + data: err.to_string(), + }), + } +} + +pub(crate) fn print(path: &String, group: &PrintGroup) -> Result<(), Error> { + let names = read_names(path)?; + + Ok(for site in names { + if group.name { + println!("{}", site.name.unwrap_or_default()); + continue; + } + if group.url { + println!("{}", site.url); + continue; + } + println!("{:?}", site); + }) +} + +pub(crate) fn add(path: &String, url: &String, name: &Option) -> Result<(), Error> { + let mut names = read_names(path)?; + let site = Site { + url: url.to_string(), + name: name.to_owned(), + }; + names.push(site); + let json = serde_json::to_string(&names).unwrap(); + std::fs::write(path, json).unwrap(); + Ok(()) +} + +pub(crate) fn remove(path: &String, url: &String) -> Result<(), Error> { + let mut names = read_names(path)?; + names.retain(|site| &site.url != url); + let json = serde_json::to_string(&names).unwrap(); + std::fs::write(path, json).unwrap(); + Ok(()) +} diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 0000000..621e6b3 --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,19 @@ +use arguments::{Arguments, Commands}; +use clap::Parser; +use commands::{add, print, remove}; +use shared::errors::Error; + +mod arguments; +mod commands; + +fn main() -> Result<(), Error> { + let args = Arguments::parse(); + + match &args.command { + Commands::Print { group } => print(&args.path, &group)?, + Commands::Add { url, name } => add(&args.path, url, name)?, + Commands::Remove { url } => remove(&args.path, url)?, + }; + + Ok(()) +} diff --git a/shared/Cargo.toml b/shared/Cargo.toml new file mode 100644 index 0000000..aa08613 --- /dev/null +++ b/shared/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "shared" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde_json = "1.0" + +[dependencies.serde] +version = "1.0" +features = ["derive"] diff --git a/shared/src/errors.rs b/shared/src/errors.rs new file mode 100644 index 0000000..8a033d5 --- /dev/null +++ b/shared/src/errors.rs @@ -0,0 +1,16 @@ +#[derive(Debug)] +pub enum ErrorStatus { + IOError, + ParsingError +} + +pub struct Error { + pub status: ErrorStatus, + pub data: String +} + +impl core::fmt::Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "A {:?} error has occured.\nDetails: {}", self.status, self.data) + } +} diff --git a/shared/src/lib.rs b/shared/src/lib.rs new file mode 100644 index 0000000..c986a43 --- /dev/null +++ b/shared/src/lib.rs @@ -0,0 +1,2 @@ +pub mod names; +pub mod errors; diff --git a/shared/src/names.rs b/shared/src/names.rs new file mode 100644 index 0000000..6408d6c --- /dev/null +++ b/shared/src/names.rs @@ -0,0 +1,19 @@ +use serde::{Deserialize, Serialize}; + +use crate::errors::{Error, ErrorStatus}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Site { + pub url: String, + pub name: Option, +} + +pub fn load_names(names: String) -> Result, Error> { + match serde_json::from_str::>(&names) { + Ok(content) => Ok(content), + Err(err) => Err(Error { + status: ErrorStatus::ParsingError, + data: err.to_string(), + }), + } +} diff --git a/src/links.rs b/src/links.rs index eab9c10..fb742a3 100644 --- a/src/links.rs +++ b/src/links.rs @@ -1,18 +1,20 @@ -static NAMES: [&str; 3] = ["mossfet.xyz", "fries.gay", "ta-kev.digital"]; +use shared::names::Site; -pub fn previous_url(source_url: &String) -> Option { - match NAMES.iter().position(|&r| r == source_url) { - Some(index) if index == 0 => Some(NAMES[NAMES.len() - 1].to_string()), - Some(index) => Some(NAMES[index - 1].to_string()), +pub fn previous_url(source_url: &String, names: &Vec) -> Option { + match names.iter().position(|r| &r.url == source_url) { + Some(index) if index == 0 => Some(names[names.len() - 1].url.to_string()), + Some(index) => Some(names[index - 1].url.to_string()), None => None, } } -pub fn next_url(source_url: &String) -> Option { +pub fn next_url(source_url: &String, names: &Vec) -> Option { // this is gay - match NAMES.iter().position(|&r| r == source_url) { - Some(index) if index == NAMES.len() - 1 => Some(NAMES[0].to_string()), - Some(index) => Some(NAMES[index + 1].to_string()), + match names.iter().position(|r| &r.url == source_url) { + Some(index) if index == names.len() - 1 => Some(names[0].url.to_string()), + Some(index) => Some(names[index + 1].url.to_string()), None => None, } } + +// TODO: previous_name, next_name diff --git a/src/main.rs b/src/main.rs index 6cb7fda..ae43a88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,10 @@ mod routes; #[launch] fn rocket() -> _ { + let names_file = std::fs::read_to_string("names.json").unwrap(); + let names = shared::names::load_names(names_file).unwrap(); rocket::build() + .manage(names) .mount( "/", routes![routes::index, routes::previous, routes::next, routes::name], diff --git a/src/routes.rs b/src/routes.rs index d263734..576669b 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,9 +1,12 @@ use crate::{links::{next_url, previous_url}, assets::ErrorTemplate}; + use rocket::{ http::Status, response::Redirect, serde::{json::Json, Serialize}, + State, }; +use shared::names::Site; const NOT_FOUND_ERROR: ErrorTemplate = ErrorTemplate { error: "Not Found", @@ -23,25 +26,27 @@ pub fn index() -> &'static str { } #[get("/previous?")] -pub fn previous(source_url: String) -> Result { - match previous_url(&source_url) { +pub fn previous(source_url: String, names: &State>) -> Result { + let names = names.inner(); + match previous_url(&source_url, names) { Some(url) => Ok(Redirect::to(format!("https://{}", url))), None => Err(Status::NotFound), } } #[get("/next?")] -pub fn next(source_url: String) -> Result { - match next_url(&source_url) { +pub fn next(source_url: String, names: &State>) -> Result { + let names = names.inner(); + match next_url(&source_url, names) { Some(url) => Ok(Redirect::to(format!("https://{}", url))), None => Err(Status::NotFound), } } #[get("/name?")] -pub fn name(source_url: String) -> Result, Status> { - let previous_site_name = previous_url(&source_url); - let next_site_name = next_url(&source_url); +pub fn name(source_url: String, names: &State>) -> Result, Status> { + let previous_site_name = previous_url(&source_url, names); + let next_site_name = next_url(&source_url, names); if previous_site_name.is_none() && next_site_name.is_none() { return Err(Status::NotFound);