aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2022-03-30 03:04:48 -0700
committerFuwn <[email protected]>2022-03-30 03:04:48 -0700
commit0495bd1837087a91a36036dbaab71c83c3acc197 (patch)
tree6f1744db3bf9a7eb07da70971f0b2aa39741a0b0 /src
downloadlocus-0495bd1837087a91a36036dbaab71c83c3acc197.tar.xz
locus-0495bd1837087a91a36036dbaab71c83c3acc197.zip
feat: 0.0.0 :star:
Diffstat (limited to 'src')
-rw-r--r--src/constants.rs55
-rw-r--r--src/macros.rs68
-rw-r--r--src/main.rs155
-rw-r--r--src/modules.rs177
4 files changed, 455 insertions, 0 deletions
diff --git a/src/constants.rs b/src/constants.rs
new file mode 100644
index 0000000..27c80bd
--- /dev/null
+++ b/src/constants.rs
@@ -0,0 +1,55 @@
+// This file is part of Locus <https://github.com/gemrest/locus>.
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-only
+
+pub const QUOTES: &[&str] = &[
+ "That brain of mine is something more than merely mortal; as time will \
+ show. - Ada Lovelace",
+ "Forget this world and all its troubles and if possible its multitudinous \
+ Charlatans — everything in short but the Enchantress of Numbers. - Ada \
+ Lovelace",
+ "If you can't give me poetry, can't you give me poetical science? - Ada \
+ Lovelace",
+ "What I cannot create, I do not understand. - Richard P. Feynman",
+ "Know how to solve every problem that has been solved. - Richard P. Feynman",
+ "Teach principles, not formulas. - Richard P. Feynman",
+ "I learned very early the difference between knowing the name of something \
+ and knowing something. - Richard P. Feynman",
+ "Scientists are explorers. Philosophers are tourists. - Richard P. Feynman",
+ "If you thought that science was certain — Well, that is just an error on \
+ your part. - Richard P. Feynman",
+ "Talk is cheap, Show me the code. - Linus Torvalds",
+ "I like offending people, because I think people who get offended should be \
+ offended. - Linus Torvalds",
+ "Only wimps use tape backup. REAL men just upload their important stuff on \
+ ftp and let the rest of the world mirror it. - Linus Torvalds",
+ "Intelligence is the ability to avoid doing work, yet getting the work \
+ done. - Linus Torvalds",
+ "Programming is not a science. Programming is a craft. - Richard Stallman",
+ "I did write some code in Java once, but that was the island in Indonesia. \
+ - Richard Stallman",
+ "Free software' is a matter of liberty, not price. To understand the \
+ concept, you should think of 'free' as in 'free speech,' not as in 'free \
+ beer'. - Richard Stallman",
+ "You can use any editor you want, but remember that vi vi vi is the text \
+ editor of the beast. - Richard Stallman",
+ "Free software is software that respects your freedom and the social \
+ solidarity of your community. So it's free as in freedom. - Richard \
+ Stallman",
+ "Sharing is good, and with digital technology, sharing is easy. - Richard \
+ Stallman",
+];
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..9104f39
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,68 @@
+// This file is part of Locus <https://github.com/gemrest/locus>.
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-only
+
+#[macro_export]
+macro_rules! mount_page {
+ ($router:ident, $at:literal, $file:literal) => {
+ ($router).mount(
+ $at,
+ Box::new(|context| {
+ Response::Success(
+ Main {
+ body: include_str!(concat!("../content/pages/", $file, ".gmi"))
+ .into(),
+ hits: &crate::hits_from_route(context.url.path()),
+ quote: QUOTES.choose(&mut rand::thread_rng()).unwrap(),
+ commit: &format!("/tree/{}", env!("VERGEN_GIT_SHA")),
+ }
+ .to_string(),
+ )
+ }),
+ );
+ };
+}
+
+#[macro_export]
+macro_rules! mount_file {
+ ($router:ident, $at:literal, $file:literal) => {
+ ($router).mount(
+ $at,
+ Box::new(|_| {
+ Response::SuccessFile(include_bytes!(concat!(
+ "../content/meta/",
+ $file
+ )))
+ }),
+ );
+ };
+}
+
+#[macro_export]
+macro_rules! success {
+ ($body:expr, $context:ident) => {
+ Response::Success(
+ Main {
+ body: &$body,
+ hits: &crate::hits_from_route($context.url.path()),
+ quote: QUOTES.choose(&mut rand::thread_rng()).unwrap(),
+ commit: &format!("/tree/{}", env!("VERGEN_GIT_SHA")),
+ }
+ .to_string(),
+ )
+ };
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..e88fd9b
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,155 @@
+// This file is part of Locus <https://github.com/gemrest/locus>.
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-only
+
+#![feature(once_cell)]
+#![deny(
+ warnings,
+ nonstandard_style,
+ unused,
+ future_incompatible,
+ rust_2018_idioms,
+ unsafe_code
+)]
+#![deny(clippy::all, clippy::nursery, clippy::pedantic)]
+#![recursion_limit = "128"]
+#![allow(clippy::cast_precision_loss)]
+
+mod constants;
+mod macros;
+mod modules;
+
+#[macro_use]
+extern crate log;
+
+use std::{lazy::SyncLazy, sync::Mutex};
+
+use constants::QUOTES;
+use pickledb::PickleDb;
+use rand::seq::SliceRandom;
+use tokio::time::Instant;
+use windmark::{Response, Router};
+use yarte::Template;
+
+static DATABASE: SyncLazy<Mutex<PickleDb>> = SyncLazy::new(|| {
+ Mutex::new({
+ if std::fs::File::open("locus.db").is_ok() {
+ PickleDb::load_json("locus.db", pickledb::PickleDbDumpPolicy::AutoDump)
+ .unwrap()
+ } else {
+ PickleDb::new_json("locus.db", pickledb::PickleDbDumpPolicy::AutoDump)
+ }
+ })
+});
+
+#[derive(Template)]
+#[template(path = "main")]
+struct Main<'a> {
+ body: &'a str,
+ hits: &'a i32,
+ quote: &'a str,
+ commit: &'a str,
+}
+
+fn hits_from_route(route: &str) -> i32 {
+ (*DATABASE.lock().unwrap()).get::<i32>(route).unwrap()
+}
+
+#[windmark::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+ std::env::set_var("RUST_LOG", "windmark,locus=trace");
+ pretty_env_logger::init();
+
+ let mut time_mount = Instant::now();
+ let mut router = Router::new();
+ let uptime = Instant::now();
+
+ router.set_private_key_file(".locus/locus_private.pem");
+ router.set_certificate_chain_file(".locus/locus_pair.pem");
+ router.set_pre_route_callback(Box::new(|stream, url, _| {
+ info!(
+ "accepted connection from {} to {}",
+ stream.peer_addr().unwrap().ip(),
+ url.to_string(),
+ );
+
+ let previous_database = (*DATABASE.lock().unwrap()).get::<i32>(url.path());
+
+ match previous_database {
+ None => {
+ (*DATABASE.lock().unwrap())
+ .set::<i32>(url.path(), &0)
+ .unwrap();
+ }
+ Some(_) => {}
+ }
+
+ let new_database = (*DATABASE.lock().unwrap()).get::<i32>(url.path());
+
+ (*DATABASE.lock().unwrap())
+ .set(url.path(), &(new_database.unwrap() + 1))
+ .unwrap();
+ }));
+ router.set_error_handler(Box::new(|_| {
+ Response::NotFound(
+ "The requested resource could not be found at this time. You can try \
+ refreshing the page, if that doesn't change anything; contact Fuwn! \
+ .into(),
+ )
+ }));
+ router.mount(
+ "/uptime",
+ Box::new(move |context| {
+ success!(
+ &format!("# UPTIME\n\n{} seconds", uptime.elapsed().as_secs()),
+ context
+ )
+ }),
+ );
+
+ info!(
+ "preliminary mounts took {}ms",
+ time_mount.elapsed().as_nanos() as f64 / 1_000_000.0
+ );
+ time_mount = Instant::now();
+
+ router.attach(modules::multi_blog);
+
+ info!(
+ "blog mounts took {}ms",
+ time_mount.elapsed().as_nanos() as f64 / 1_000_000.0
+ );
+ time_mount = Instant::now();
+
+ mount_file!(router, "/robots.txt", "robots.txt");
+ mount_file!(router, "/favicon.txt", "favicon.txt");
+ mount_page!(router, "/", "index");
+ mount_page!(router, "/contact", "contact");
+ mount_page!(router, "/donate", "donate");
+ mount_page!(router, "/gemini", "gemini");
+ mount_page!(router, "/gopher", "gopher");
+ mount_page!(router, "/interests", "interests");
+ mount_page!(router, "/skills", "skills");
+
+ info!(
+ "static mounts took {}ms",
+ time_mount.elapsed().as_nanos() as f64 / 1_000_000.0
+ );
+
+ router.run().await
+}
diff --git a/src/modules.rs b/src/modules.rs
new file mode 100644
index 0000000..0067266
--- /dev/null
+++ b/src/modules.rs
@@ -0,0 +1,177 @@
+// This file is part of Locus <https://github.com/gemrest/locus>.
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-only
+
+use std::{
+ collections::HashMap,
+ fs::{self, read_dir},
+ io::Read,
+};
+
+use rand::seq::SliceRandom;
+use windmark::Response;
+
+use crate::{success, Main, QUOTES};
+
+#[allow(clippy::too_many_lines)]
+pub fn multi_blog(router: &mut windmark::Router) {
+ let paths = read_dir("content/pages/blog").unwrap();
+ let mut blogs: HashMap<String, HashMap<_, _>> = HashMap::new();
+
+ for path in paths {
+ let blog = path.unwrap().path().display().to_string();
+ let blog_paths = read_dir(&blog).unwrap();
+ let mut entries: HashMap<_, String> = HashMap::new();
+
+ blog_paths
+ .map(|e| e.unwrap().path().display().to_string())
+ .for_each(|file| {
+ let mut contents = String::new();
+
+ fs::File::open(&file)
+ .unwrap()
+ .read_to_string(&mut contents)
+ .unwrap();
+
+ entries.insert(
+ file.replace(&blog, "").replace(".gmi", "").replace(
+ {
+ #[cfg(windows)]
+ {
+ '\\'
+ }
+
+ #[cfg(unix)]
+ {
+ '/'
+ }
+ },
+ "",
+ ),
+ contents,
+ );
+ });
+
+ blogs.insert(
+ blog
+ .replace(
+ {
+ #[cfg(windows)]
+ {
+ "content/pages/blog\\"
+ }
+
+ #[cfg(unix)]
+ {
+ "content/pages/blog/"
+ }
+ },
+ "",
+ )
+ .split('_')
+ .map(|s| {
+ // https://gist.github.com/jpastuszek/2704f3c5a3864b05c48ee688d0fd21d7
+ let mut c = s.chars();
+
+ match c.next() {
+ None => String::new(),
+ Some(f) =>
+ f.to_uppercase()
+ .chain(c.flat_map(char::to_lowercase))
+ .collect(),
+ }
+ })
+ .collect::<Vec<_>>()
+ .join(" "),
+ entries,
+ );
+ }
+
+ let blog_clone = blogs.clone();
+
+ router.mount(
+ "/blog",
+ Box::new(move |context| {
+ success!(
+ &format!(
+ "# BLOGS ({})\n\n{}",
+ blog_clone.len(),
+ blog_clone
+ .iter()
+ .map(|(title, _)| title.clone())
+ .collect::<Vec<_>>()
+ .into_iter()
+ .map(|i| {
+ format!(
+ "=> {} {}",
+ format_args!("/blog/{}", i.replace(' ', "_").to_lowercase(),),
+ i
+ )
+ })
+ .collect::<Vec<_>>()
+ .join("\n")
+ ),
+ context
+ )
+ }),
+ );
+
+ for (blog, entries) in blogs {
+ let fixed_blog_name = blog.replace(' ', "_").to_lowercase();
+ let entries_clone = entries.clone();
+ let fixed_blog_name_clone = fixed_blog_name.clone();
+
+ router.mount(
+ &format!("/blog/{}", fixed_blog_name),
+ Box::new(move |context| {
+ success!(
+ &format!(
+ "# {} ({})\n\n{}",
+ blog.to_uppercase(),
+ entries_clone.len(),
+ entries_clone
+ .iter()
+ .map(|(title, _)| title.clone())
+ .collect::<Vec<_>>()
+ .into_iter()
+ .map(|i| {
+ format!(
+ "=> {} {}",
+ format_args!(
+ "/blog/{}/{}",
+ fixed_blog_name_clone,
+ i.to_lowercase()
+ ),
+ i
+ )
+ })
+ .collect::<Vec<_>>()
+ .join("\n")
+ ),
+ context
+ )
+ }),
+ );
+
+ for (title, contents) in entries {
+ router.mount(
+ &format!("/blog/{}/{}", fixed_blog_name, title.to_lowercase()),
+ Box::new(move |context| success!(contents, context)),
+ );
+ }
+ }
+}