From 474b4cafabce2d02d722198327df68f5c9a27ced Mon Sep 17 00:00:00 2001 From: Karcsesz Date: Sun, 17 Mar 2024 21:56:56 +0100 Subject: [PATCH] Documentation pass --- src/editor/commands.rs | 1 + src/editor/commands/editor.rs | 3 +++ src/editor/commands/fetch.rs | 1 - src/editor/finger_remote.rs | 8 +++++--- src/editor/mod.rs | 1 + src/editor/open_in_editor.rs | 7 +++++-- src/editor/try_reload_server.rs | 8 +++++++- src/schema/lookup_handler.rs | 23 ++++++++++++++++++++--- src/schema/mod.rs | 2 ++ src/schema/resource.rs | 26 +++++++++++++++++++------- src/schema/resource_list.rs | 17 ++++++++++++++--- 11 files changed, 77 insertions(+), 20 deletions(-) diff --git a/src/editor/commands.rs b/src/editor/commands.rs index 74565a7..40c5528 100644 --- a/src/editor/commands.rs +++ b/src/editor/commands.rs @@ -1,3 +1,4 @@ +//! Commands to run from the command line other than launching a server instance pub mod editor; pub mod fetch; pub mod init; diff --git a/src/editor/commands/editor.rs b/src/editor/commands/editor.rs index 4bc2d22..b054738 100644 --- a/src/editor/commands/editor.rs +++ b/src/editor/commands/editor.rs @@ -1,3 +1,6 @@ +//! Command for editing the record associated with a provided query string +//! in the system-defined editor + use crate::args_parser::SaveSettings; use crate::editor::open_in_editor::open_resource_in_editor; use crate::schema::lookup_handler::LookupHandler; diff --git a/src/editor/commands/fetch.rs b/src/editor/commands/fetch.rs index 8bf667c..5fceb20 100644 --- a/src/editor/commands/fetch.rs +++ b/src/editor/commands/fetch.rs @@ -1,7 +1,6 @@ use crate::args_parser::SaveSettings; use crate::editor::finger_remote::finger_many_fedi; use crate::schema::resource_list::ResourceList; -use std::io::{BufRead, BufReader}; use std::path::PathBuf; use tracing::{debug, error, info, instrument, trace, warn}; diff --git a/src/editor/finger_remote.rs b/src/editor/finger_remote.rs index fb9f244..92456eb 100644 --- a/src/editor/finger_remote.rs +++ b/src/editor/finger_remote.rs @@ -1,3 +1,5 @@ +//! Utilities for running a WebFinger query against a remote target + use crate::editor::finger_remote::FediFingerError::UrlBuildFailed; use crate::schema::resource::Resource; use reqwest::blocking::Client; @@ -6,7 +8,7 @@ use std::fmt::Display; use thiserror::Error; use tracing::{debug, error, instrument}; -/// Error type returned by `finger_url` +/// Error type returned by [`finger_url`] #[derive(Debug, Error)] #[allow(clippy::enum_variant_names)] pub enum FingerError { @@ -18,7 +20,7 @@ pub enum FingerError { ParseFailed(reqwest::Error), } -/// Run a WebFinger request at the provided URL and parse it as a `Resource` +/// Run a WebFinger request at the provided URL and parse it as a [`Resource`] #[instrument(skip(client))] pub fn finger_url(client: &Client, url: Url) -> Result { use FingerError::*; @@ -31,7 +33,7 @@ pub fn finger_url(client: &Client, url: Url) -> Result { Ok(response) } -/// Error type returned by `finger_fedi` +/// Error type returned by [`finger_fedi`] #[derive(Debug, Error)] pub enum FediFingerError { #[error("Couldn't find the middle @ symbol")] diff --git a/src/editor/mod.rs b/src/editor/mod.rs index 8f9523e..aaacafa 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -1,3 +1,4 @@ +//! Utilities for editing records in a database stored on disk pub mod commands; pub mod finger_remote; pub mod open_in_editor; diff --git a/src/editor/open_in_editor.rs b/src/editor/open_in_editor.rs index 4f7599e..75a90ed 100644 --- a/src/editor/open_in_editor.rs +++ b/src/editor/open_in_editor.rs @@ -1,3 +1,5 @@ +//! Utilities for opening a string in the system-defined editor + use crate::schema::resource::Resource; use std::fmt::{Debug, Display}; use std::io::{Read, Seek, SeekFrom, Write}; @@ -6,7 +8,7 @@ use tempfile::NamedTempFile; use thiserror::Error; use tracing::{debug, instrument, trace, warn}; -/// Error type returned by `spawn_editor` +/// Error type returned by [`spawn_editor`] #[derive(Debug, Error)] pub enum EditorSpawnError { #[error("failed to parse out absolute path of editor: {0}")] @@ -27,7 +29,8 @@ pub enum EditorSpawnError { BufferReadbackFailed(std::io::Error), } -/// Spawn the system editor to edit the provided `buffer`. Returns the edited buffer or an error if something goes wrong +/// Spawn the system editor to edit the provided `buffer`. +/// Returns the edited buffer or an error if something goes wrong #[instrument(skip_all)] pub fn spawn_editor(buffer: impl AsRef + Display) -> Result { use EditorSpawnError::*; diff --git a/src/editor/try_reload_server.rs b/src/editor/try_reload_server.rs index e43fd93..31cef10 100644 --- a/src/editor/try_reload_server.rs +++ b/src/editor/try_reload_server.rs @@ -1,3 +1,5 @@ +//! Utility functions for attempting to reload a running server instance of the application + use crate::args_parser::ServerReloadOptions; use nix::sys::signal::{kill, Signal}; use nix::unistd::Pid; @@ -7,8 +9,9 @@ use std::str::FromStr; use thiserror::Error; use tracing::{error, info}; +/// Error type returned by [`try_reload_server`] and [`load_pid`] #[derive(Debug, Error)] -pub enum ServerReloadError { +enum ServerReloadError { #[error("Couldn't find PID file at {0}")] PIDFileNotFound(String), #[error("Failed to open PID file: {0}")] @@ -21,6 +24,7 @@ pub enum ServerReloadError { FailedToSendSignal(#[from] nix::errno::Errno), } +/// Tries to load and parse the PID file at the provided path fn load_pid(pid_file_path: &Path) -> Result { use ServerReloadError::*; if !pid_file_path.exists() { @@ -34,12 +38,14 @@ fn load_pid(pid_file_path: &Path) -> Result { Ok(Pid::from_raw(pid)) } +/// Tries to send a reload command to the server instance fn try_reload_server(pid_file_path: &Path) -> Result<(), ServerReloadError> { let pid = load_pid(pid_file_path)?; kill(pid, Signal::SIGHUP)?; Ok(()) } +/// Attempts to reload an already running server instance of the application, logging an error if it was unsuccessful. pub fn reload(pid_file_path: PathBuf, options: ServerReloadOptions) { if options.reload_server { info!("Attempting to reload server..."); diff --git a/src/schema/lookup_handler.rs b/src/schema/lookup_handler.rs index 6054d16..321c6c3 100644 --- a/src/schema/lookup_handler.rs +++ b/src/schema/lookup_handler.rs @@ -1,3 +1,5 @@ +//! Struct for storing WebFinger resources and running queries against them + use crate::schema::resource::Resource; use crate::schema::resource_list::{ResourceList, ResourceLoadError}; use std::collections::HashMap; @@ -25,25 +27,31 @@ pub enum DataLoadError { } impl LookupHandler { - /// Load and prepare a new LookupHandler from the file at `path` + /// Load and prepare a new [`LookupHandler`] from the file at `path` #[instrument(level = "debug", skip(path))] pub fn load(path: impl AsRef + Debug) -> Result { Self::build_from_resource_list(ResourceList::load(path)?) } + /// Load and prepare a new [`LookupHandler`] from the provided reader #[cfg(test)] fn load_from_reader(reader: impl std::io::Read) -> Result { Self::build_from_resource_list(ResourceList::load_from_reader(reader)?) } + /// Build a new [`LookupHandler`] from the provided [`ResourceList`] fn build_from_resource_list(resources: ResourceList) -> Result { - let mut lookup = HashMap::new(); debug!("Building lookup map..."); + + let mut lookup = HashMap::new(); for (index, resource) in resources.0.iter().enumerate() { for lookup_to_add in resource.keys() { let lookup_to_add = lookup_to_add.to_lowercase(); + debug!("Adding {lookup_to_add} for {}", resource.subject); - if let Some(duplicate) = lookup.insert(lookup_to_add.clone(), index) { + let duplicate = lookup.insert(lookup_to_add.clone(), index); + + if let Some(duplicate) = duplicate { return Err(DataLoadError::DuplicateLookupFound { duplicated: lookup_to_add, subjects: [ @@ -54,24 +62,33 @@ impl LookupHandler { } } } + info!("Aggregated {} lookup strings", lookup.len()); Ok(LookupHandler { resources, lookup }) } + /// Run the provided `resource_query` on the index and return a borrow of the returned resource #[instrument(level = "debug")] pub fn lookup(&self, resource_query: &str) -> Option<&Resource> { self.lookup_with_index(resource_query) .map(|(_index, resource)| resource) } + /// Run the provided `resource_query` on the index and + /// return a borrow of the returned resource and its index in the database pub fn lookup_with_index(&self, resource_query: &str) -> Option<(usize, &Resource)> { let resource_query = resource_query.to_lowercase(); + let resource_index = *self.lookup.get(resource_query.as_str())?; + let found_resource = &self.resources.0[resource_index]; + debug!("Lookup for {resource_query} returned {found_resource:?}"); Some((resource_index, found_resource)) } + /// Convert the [`LookupHandler`] into its internal [`ResourceList`] + #[allow(dead_code)] pub fn into_inner(self) -> ResourceList { self.resources } diff --git a/src/schema/mod.rs b/src/schema/mod.rs index 485aae9..cca6a56 100644 --- a/src/schema/mod.rs +++ b/src/schema/mod.rs @@ -1,3 +1,5 @@ +//! "Schema" of the internal representation of WebFinger data + pub mod lookup_handler; pub mod resource; pub mod resource_list; diff --git a/src/schema/resource.rs b/src/schema/resource.rs index ae2e179..ed77adc 100644 --- a/src/schema/resource.rs +++ b/src/schema/resource.rs @@ -1,3 +1,5 @@ +//! A single WebFinger resource and associated utility functions + use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fmt::{Debug, Display, Formatter}; @@ -6,11 +8,15 @@ use tracing::debug; /// A single WebFinger resource #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Resource { + /// The subject of this resource pub subject: String, + /// Known aliases of the resource to also respond to #[serde(skip_serializing_if = "Option::is_none")] pub aliases: Option>, + /// List of properties associated with the resource #[serde(skip_serializing_if = "Option::is_none")] pub properties: Option>>, + /// Links associated with the resource #[serde(skip_serializing_if = "Option::is_none")] pub links: Option>, } @@ -23,6 +29,7 @@ impl Display for Resource { } impl Resource { + /// Creates a new completely blank WebFinger resource with the provided subject pub fn new(subject: String) -> Self { Self { subject, @@ -33,7 +40,7 @@ impl Resource { } /// Returns the aliases of the given record. If the `aliases` field is - /// entirely missing, returns &[]. + /// entirely missing, returns an empty array. pub fn keys(&self) -> impl Iterator { let aliases = if let Some(aliases) = &self.aliases { aliases.as_slice() @@ -44,13 +51,18 @@ impl Resource { aliases.iter().chain(std::iter::once(&self.subject)) } + /// Replaces the current `subject` field of the WebFinger resource + /// with a new value and pushes the old value into the aliases field + /// if it is not already present there pub fn add_new_primary_subject(&mut self, mut subject: String) { if subject == self.subject { debug!("New and old subjects match, skipping..."); return; } + debug!("Swapping new and old subject"); std::mem::swap(&mut subject, &mut self.subject); + debug!("Pushing current subject into aliases"); if let Some(ref mut aliases) = self.aliases { if !aliases.contains(&subject) { @@ -147,14 +159,13 @@ pub struct Link { /// Functions to generate data for testing functions that manipulate `Resource` structs pub mod test_data { use crate::schema::resource::Resource; + + /// A [`Resource`] with only the `subject` field set pub fn barebones_user() -> Resource { - Resource { - subject: "acct:user@domain.tld".to_string(), - aliases: None, - properties: None, - links: None, - } + Resource::new("acct:user@domain.tld".to_string()) } + + /// A default [`Resource`] with a single alias pub fn user_with_single_alias() -> Resource { Resource { subject: "acct:user@domain.tld".to_string(), @@ -164,6 +175,7 @@ pub mod test_data { } } + /// A default [`Resource`] with a single alias that matches the subject pub fn user_with_matching_subject_and_alias() -> Resource { Resource { subject: "acct:user@domain.tld".to_string(), diff --git a/src/schema/resource_list.rs b/src/schema/resource_list.rs index 28b949e..69ed685 100644 --- a/src/schema/resource_list.rs +++ b/src/schema/resource_list.rs @@ -1,3 +1,5 @@ +//! An array of [`Resource`]s with a builtin way to serialise and deserialise to file and/or stream + use crate::args_parser::CollisionHandling; use crate::schema::resource::Resource; use std::collections::HashSet; @@ -6,9 +8,11 @@ use std::path::Path; use thiserror::Error; use tracing::{debug, info, instrument, trace, warn}; +/// A dynamic array of [`Resource`]s #[derive(Debug, Clone)] pub struct ResourceList(pub Vec); +/// Error type returned by [`ResourceList::load`] and [`ResourceList::load_from_reader`] #[derive(Debug, Error)] pub enum ResourceLoadError { #[error("failed to open the resource database: {0}")] @@ -17,7 +21,9 @@ pub enum ResourceLoadError { FileParse(#[from] serde_json::Error), } +/// Error type returned by [`ResourceList::save`] and [`ResourceList::save_to_writer`] #[derive(Debug, Error)] +#[allow(unused)] pub enum ResourceSaveError { #[error("Failed to open the resource database for writing: {0}")] FileOpen(std::io::Error), @@ -28,7 +34,7 @@ pub enum ResourceSaveError { } impl ResourceList { - /// Loads the `Resource`s from the given `path` + /// Loads the [`Resource`]s from the given `path` #[instrument(level = "debug")] pub fn load(path: impl AsRef + Debug) -> Result { info!("Loading data from {path:?}..."); @@ -36,7 +42,7 @@ impl ResourceList { Self::load_from_reader(file) } - /// Loads the `Resource`s from the given reader + /// Loads the [`Resource`]s from the given reader pub fn load_from_reader(reader: impl std::io::Read) -> Result { let reader = std::io::BufReader::new(reader); debug!("Parsing as JSON..."); @@ -46,6 +52,8 @@ impl ResourceList { Ok(Self(resources)) } + /// Save the [`Resource`]s to the given `path`, backing up the file if it is already present + /// to `path.bak` #[instrument(level = "debug", skip(path, self))] pub fn save(&self, path: impl AsRef) -> Result<(), ResourceSaveError> { info!("Creating backup before writing..."); @@ -57,6 +65,7 @@ impl ResourceList { self.save_to_writer(file) } + /// Save the [`Resource`]s into the given `writer` pub fn save_to_writer(&self, writer: impl std::io::Write) -> Result<(), ResourceSaveError> { trace!("{self:?}"); let writer = std::io::BufWriter::new(writer); @@ -64,7 +73,8 @@ impl ResourceList { Ok(serde_json::to_writer(writer, &self.0)?) } - /// Merges `new_records` into the `ResourceList` with lookup collisions being handled as defined in `collision_handling` + /// Merges `new_records` into the [`ResourceList`] with lookup collisions + /// being handled as defined in `collision_handling` #[instrument(level = "debug", skip(self, new_records))] pub fn merge_records( &mut self, @@ -74,6 +84,7 @@ impl ResourceList { debug!("Building hashset of already taken queries..."); let unique_check: HashSet = HashSet::from_iter(self.0.iter().flat_map(Resource::keys).cloned()); + for record in new_records { let record_keys = HashSet::from_iter(record.keys().cloned()); let collisions = unique_check