Documentation pass
This commit is contained in:
parent
78136ac622
commit
474b4cafab
11 changed files with 77 additions and 20 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
//! Commands to run from the command line other than launching a server instance
|
||||||
pub mod editor;
|
pub mod editor;
|
||||||
pub mod fetch;
|
pub mod fetch;
|
||||||
pub mod init;
|
pub mod init;
|
||||||
|
|
|
@ -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::args_parser::SaveSettings;
|
||||||
use crate::editor::open_in_editor::open_resource_in_editor;
|
use crate::editor::open_in_editor::open_resource_in_editor;
|
||||||
use crate::schema::lookup_handler::LookupHandler;
|
use crate::schema::lookup_handler::LookupHandler;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::args_parser::SaveSettings;
|
use crate::args_parser::SaveSettings;
|
||||||
use crate::editor::finger_remote::finger_many_fedi;
|
use crate::editor::finger_remote::finger_many_fedi;
|
||||||
use crate::schema::resource_list::ResourceList;
|
use crate::schema::resource_list::ResourceList;
|
||||||
use std::io::{BufRead, BufReader};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing::{debug, error, info, instrument, trace, warn};
|
use tracing::{debug, error, info, instrument, trace, warn};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Utilities for running a WebFinger query against a remote target
|
||||||
|
|
||||||
use crate::editor::finger_remote::FediFingerError::UrlBuildFailed;
|
use crate::editor::finger_remote::FediFingerError::UrlBuildFailed;
|
||||||
use crate::schema::resource::Resource;
|
use crate::schema::resource::Resource;
|
||||||
use reqwest::blocking::Client;
|
use reqwest::blocking::Client;
|
||||||
|
@ -6,7 +8,7 @@ use std::fmt::Display;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{debug, error, instrument};
|
use tracing::{debug, error, instrument};
|
||||||
|
|
||||||
/// Error type returned by `finger_url`
|
/// Error type returned by [`finger_url`]
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
pub enum FingerError {
|
pub enum FingerError {
|
||||||
|
@ -18,7 +20,7 @@ pub enum FingerError {
|
||||||
ParseFailed(reqwest::Error),
|
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))]
|
#[instrument(skip(client))]
|
||||||
pub fn finger_url(client: &Client, url: Url) -> Result<Resource, FingerError> {
|
pub fn finger_url(client: &Client, url: Url) -> Result<Resource, FingerError> {
|
||||||
use FingerError::*;
|
use FingerError::*;
|
||||||
|
@ -31,7 +33,7 @@ pub fn finger_url(client: &Client, url: Url) -> Result<Resource, FingerError> {
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error type returned by `finger_fedi`
|
/// Error type returned by [`finger_fedi`]
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum FediFingerError {
|
pub enum FediFingerError {
|
||||||
#[error("Couldn't find the middle @ symbol")]
|
#[error("Couldn't find the middle @ symbol")]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//! Utilities for editing records in a database stored on disk
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod finger_remote;
|
pub mod finger_remote;
|
||||||
pub mod open_in_editor;
|
pub mod open_in_editor;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Utilities for opening a string in the system-defined editor
|
||||||
|
|
||||||
use crate::schema::resource::Resource;
|
use crate::schema::resource::Resource;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
@ -6,7 +8,7 @@ use tempfile::NamedTempFile;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{debug, instrument, trace, warn};
|
use tracing::{debug, instrument, trace, warn};
|
||||||
|
|
||||||
/// Error type returned by `spawn_editor`
|
/// Error type returned by [`spawn_editor`]
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum EditorSpawnError {
|
pub enum EditorSpawnError {
|
||||||
#[error("failed to parse out absolute path of editor: {0}")]
|
#[error("failed to parse out absolute path of editor: {0}")]
|
||||||
|
@ -27,7 +29,8 @@ pub enum EditorSpawnError {
|
||||||
BufferReadbackFailed(std::io::Error),
|
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)]
|
#[instrument(skip_all)]
|
||||||
pub fn spawn_editor(buffer: impl AsRef<str> + Display) -> Result<String, EditorSpawnError> {
|
pub fn spawn_editor(buffer: impl AsRef<str> + Display) -> Result<String, EditorSpawnError> {
|
||||||
use EditorSpawnError::*;
|
use EditorSpawnError::*;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Utility functions for attempting to reload a running server instance of the application
|
||||||
|
|
||||||
use crate::args_parser::ServerReloadOptions;
|
use crate::args_parser::ServerReloadOptions;
|
||||||
use nix::sys::signal::{kill, Signal};
|
use nix::sys::signal::{kill, Signal};
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
|
@ -7,8 +9,9 @@ use std::str::FromStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
/// Error type returned by [`try_reload_server`] and [`load_pid`]
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ServerReloadError {
|
enum ServerReloadError {
|
||||||
#[error("Couldn't find PID file at {0}")]
|
#[error("Couldn't find PID file at {0}")]
|
||||||
PIDFileNotFound(String),
|
PIDFileNotFound(String),
|
||||||
#[error("Failed to open PID file: {0}")]
|
#[error("Failed to open PID file: {0}")]
|
||||||
|
@ -21,6 +24,7 @@ pub enum ServerReloadError {
|
||||||
FailedToSendSignal(#[from] nix::errno::Errno),
|
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<Pid, ServerReloadError> {
|
fn load_pid(pid_file_path: &Path) -> Result<Pid, ServerReloadError> {
|
||||||
use ServerReloadError::*;
|
use ServerReloadError::*;
|
||||||
if !pid_file_path.exists() {
|
if !pid_file_path.exists() {
|
||||||
|
@ -34,12 +38,14 @@ fn load_pid(pid_file_path: &Path) -> Result<Pid, ServerReloadError> {
|
||||||
Ok(Pid::from_raw(pid))
|
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> {
|
fn try_reload_server(pid_file_path: &Path) -> Result<(), ServerReloadError> {
|
||||||
let pid = load_pid(pid_file_path)?;
|
let pid = load_pid(pid_file_path)?;
|
||||||
kill(pid, Signal::SIGHUP)?;
|
kill(pid, Signal::SIGHUP)?;
|
||||||
Ok(())
|
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) {
|
pub fn reload(pid_file_path: PathBuf, options: ServerReloadOptions) {
|
||||||
if options.reload_server {
|
if options.reload_server {
|
||||||
info!("Attempting to reload server...");
|
info!("Attempting to reload server...");
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Struct for storing WebFinger resources and running queries against them
|
||||||
|
|
||||||
use crate::schema::resource::Resource;
|
use crate::schema::resource::Resource;
|
||||||
use crate::schema::resource_list::{ResourceList, ResourceLoadError};
|
use crate::schema::resource_list::{ResourceList, ResourceLoadError};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -25,25 +27,31 @@ pub enum DataLoadError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LookupHandler {
|
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))]
|
#[instrument(level = "debug", skip(path))]
|
||||||
pub fn load(path: impl AsRef<Path> + Debug) -> Result<Self, DataLoadError> {
|
pub fn load(path: impl AsRef<Path> + Debug) -> Result<Self, DataLoadError> {
|
||||||
Self::build_from_resource_list(ResourceList::load(path)?)
|
Self::build_from_resource_list(ResourceList::load(path)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load and prepare a new [`LookupHandler`] from the provided reader
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn load_from_reader(reader: impl std::io::Read) -> Result<Self, DataLoadError> {
|
fn load_from_reader(reader: impl std::io::Read) -> Result<Self, DataLoadError> {
|
||||||
Self::build_from_resource_list(ResourceList::load_from_reader(reader)?)
|
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<Self, DataLoadError> {
|
fn build_from_resource_list(resources: ResourceList) -> Result<Self, DataLoadError> {
|
||||||
let mut lookup = HashMap::new();
|
|
||||||
debug!("Building lookup map...");
|
debug!("Building lookup map...");
|
||||||
|
|
||||||
|
let mut lookup = HashMap::new();
|
||||||
for (index, resource) in resources.0.iter().enumerate() {
|
for (index, resource) in resources.0.iter().enumerate() {
|
||||||
for lookup_to_add in resource.keys() {
|
for lookup_to_add in resource.keys() {
|
||||||
let lookup_to_add = lookup_to_add.to_lowercase();
|
let lookup_to_add = lookup_to_add.to_lowercase();
|
||||||
|
|
||||||
debug!("Adding {lookup_to_add} for {}", resource.subject);
|
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 {
|
return Err(DataLoadError::DuplicateLookupFound {
|
||||||
duplicated: lookup_to_add,
|
duplicated: lookup_to_add,
|
||||||
subjects: [
|
subjects: [
|
||||||
|
@ -54,24 +62,33 @@ impl LookupHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Aggregated {} lookup strings", lookup.len());
|
info!("Aggregated {} lookup strings", lookup.len());
|
||||||
Ok(LookupHandler { resources, lookup })
|
Ok(LookupHandler { resources, lookup })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run the provided `resource_query` on the index and return a borrow of the returned resource
|
||||||
#[instrument(level = "debug")]
|
#[instrument(level = "debug")]
|
||||||
pub fn lookup(&self, resource_query: &str) -> Option<&Resource> {
|
pub fn lookup(&self, resource_query: &str) -> Option<&Resource> {
|
||||||
self.lookup_with_index(resource_query)
|
self.lookup_with_index(resource_query)
|
||||||
.map(|(_index, resource)| resource)
|
.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)> {
|
pub fn lookup_with_index(&self, resource_query: &str) -> Option<(usize, &Resource)> {
|
||||||
let resource_query = resource_query.to_lowercase();
|
let resource_query = resource_query.to_lowercase();
|
||||||
|
|
||||||
let resource_index = *self.lookup.get(resource_query.as_str())?;
|
let resource_index = *self.lookup.get(resource_query.as_str())?;
|
||||||
|
|
||||||
let found_resource = &self.resources.0[resource_index];
|
let found_resource = &self.resources.0[resource_index];
|
||||||
|
|
||||||
debug!("Lookup for {resource_query} returned {found_resource:?}");
|
debug!("Lookup for {resource_query} returned {found_resource:?}");
|
||||||
Some((resource_index, found_resource))
|
Some((resource_index, found_resource))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the [`LookupHandler`] into its internal [`ResourceList`]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn into_inner(self) -> ResourceList {
|
pub fn into_inner(self) -> ResourceList {
|
||||||
self.resources
|
self.resources
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! "Schema" of the internal representation of WebFinger data
|
||||||
|
|
||||||
pub mod lookup_handler;
|
pub mod lookup_handler;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
pub mod resource_list;
|
pub mod resource_list;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! A single WebFinger resource and associated utility functions
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
@ -6,11 +8,15 @@ use tracing::debug;
|
||||||
/// A single WebFinger resource
|
/// A single WebFinger resource
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Resource {
|
pub struct Resource {
|
||||||
|
/// The subject of this resource
|
||||||
pub subject: String,
|
pub subject: String,
|
||||||
|
/// Known aliases of the resource to also respond to
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub aliases: Option<Vec<String>>,
|
pub aliases: Option<Vec<String>>,
|
||||||
|
/// List of properties associated with the resource
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub properties: Option<HashMap<String, Option<String>>>,
|
pub properties: Option<HashMap<String, Option<String>>>,
|
||||||
|
/// Links associated with the resource
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub links: Option<Vec<Link>>,
|
pub links: Option<Vec<Link>>,
|
||||||
}
|
}
|
||||||
|
@ -23,6 +29,7 @@ impl Display for Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resource {
|
impl Resource {
|
||||||
|
/// Creates a new completely blank WebFinger resource with the provided subject
|
||||||
pub fn new(subject: String) -> Self {
|
pub fn new(subject: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
subject,
|
subject,
|
||||||
|
@ -33,7 +40,7 @@ impl Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the aliases of the given record. If the `aliases` field is
|
/// 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<Item = &String> {
|
pub fn keys(&self) -> impl Iterator<Item = &String> {
|
||||||
let aliases = if let Some(aliases) = &self.aliases {
|
let aliases = if let Some(aliases) = &self.aliases {
|
||||||
aliases.as_slice()
|
aliases.as_slice()
|
||||||
|
@ -44,13 +51,18 @@ impl Resource {
|
||||||
aliases.iter().chain(std::iter::once(&self.subject))
|
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) {
|
pub fn add_new_primary_subject(&mut self, mut subject: String) {
|
||||||
if subject == self.subject {
|
if subject == self.subject {
|
||||||
debug!("New and old subjects match, skipping...");
|
debug!("New and old subjects match, skipping...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Swapping new and old subject");
|
debug!("Swapping new and old subject");
|
||||||
std::mem::swap(&mut subject, &mut self.subject);
|
std::mem::swap(&mut subject, &mut self.subject);
|
||||||
|
|
||||||
debug!("Pushing current subject into aliases");
|
debug!("Pushing current subject into aliases");
|
||||||
if let Some(ref mut aliases) = self.aliases {
|
if let Some(ref mut aliases) = self.aliases {
|
||||||
if !aliases.contains(&subject) {
|
if !aliases.contains(&subject) {
|
||||||
|
@ -147,14 +159,13 @@ pub struct Link {
|
||||||
/// Functions to generate data for testing functions that manipulate `Resource` structs
|
/// Functions to generate data for testing functions that manipulate `Resource` structs
|
||||||
pub mod test_data {
|
pub mod test_data {
|
||||||
use crate::schema::resource::Resource;
|
use crate::schema::resource::Resource;
|
||||||
|
|
||||||
|
/// A [`Resource`] with only the `subject` field set
|
||||||
pub fn barebones_user() -> Resource {
|
pub fn barebones_user() -> Resource {
|
||||||
Resource {
|
Resource::new("acct:user@domain.tld".to_string())
|
||||||
subject: "acct:user@domain.tld".to_string(),
|
|
||||||
aliases: None,
|
|
||||||
properties: None,
|
|
||||||
links: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A default [`Resource`] with a single alias
|
||||||
pub fn user_with_single_alias() -> Resource {
|
pub fn user_with_single_alias() -> Resource {
|
||||||
Resource {
|
Resource {
|
||||||
subject: "acct:user@domain.tld".to_string(),
|
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 {
|
pub fn user_with_matching_subject_and_alias() -> Resource {
|
||||||
Resource {
|
Resource {
|
||||||
subject: "acct:user@domain.tld".to_string(),
|
subject: "acct:user@domain.tld".to_string(),
|
||||||
|
|
|
@ -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::args_parser::CollisionHandling;
|
||||||
use crate::schema::resource::Resource;
|
use crate::schema::resource::Resource;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
@ -6,9 +8,11 @@ use std::path::Path;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{debug, info, instrument, trace, warn};
|
use tracing::{debug, info, instrument, trace, warn};
|
||||||
|
|
||||||
|
/// A dynamic array of [`Resource`]s
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ResourceList(pub Vec<Resource>);
|
pub struct ResourceList(pub Vec<Resource>);
|
||||||
|
|
||||||
|
/// Error type returned by [`ResourceList::load`] and [`ResourceList::load_from_reader`]
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ResourceLoadError {
|
pub enum ResourceLoadError {
|
||||||
#[error("failed to open the resource database: {0}")]
|
#[error("failed to open the resource database: {0}")]
|
||||||
|
@ -17,7 +21,9 @@ pub enum ResourceLoadError {
|
||||||
FileParse(#[from] serde_json::Error),
|
FileParse(#[from] serde_json::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error type returned by [`ResourceList::save`] and [`ResourceList::save_to_writer`]
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
#[allow(unused)]
|
||||||
pub enum ResourceSaveError {
|
pub enum ResourceSaveError {
|
||||||
#[error("Failed to open the resource database for writing: {0}")]
|
#[error("Failed to open the resource database for writing: {0}")]
|
||||||
FileOpen(std::io::Error),
|
FileOpen(std::io::Error),
|
||||||
|
@ -28,7 +34,7 @@ pub enum ResourceSaveError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResourceList {
|
impl ResourceList {
|
||||||
/// Loads the `Resource`s from the given `path`
|
/// Loads the [`Resource`]s from the given `path`
|
||||||
#[instrument(level = "debug")]
|
#[instrument(level = "debug")]
|
||||||
pub fn load(path: impl AsRef<Path> + Debug) -> Result<Self, ResourceLoadError> {
|
pub fn load(path: impl AsRef<Path> + Debug) -> Result<Self, ResourceLoadError> {
|
||||||
info!("Loading data from {path:?}...");
|
info!("Loading data from {path:?}...");
|
||||||
|
@ -36,7 +42,7 @@ impl ResourceList {
|
||||||
Self::load_from_reader(file)
|
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<Self, ResourceLoadError> {
|
pub fn load_from_reader(reader: impl std::io::Read) -> Result<Self, ResourceLoadError> {
|
||||||
let reader = std::io::BufReader::new(reader);
|
let reader = std::io::BufReader::new(reader);
|
||||||
debug!("Parsing as JSON...");
|
debug!("Parsing as JSON...");
|
||||||
|
@ -46,6 +52,8 @@ impl ResourceList {
|
||||||
Ok(Self(resources))
|
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))]
|
#[instrument(level = "debug", skip(path, self))]
|
||||||
pub fn save(&self, path: impl AsRef<Path>) -> Result<(), ResourceSaveError> {
|
pub fn save(&self, path: impl AsRef<Path>) -> Result<(), ResourceSaveError> {
|
||||||
info!("Creating backup before writing...");
|
info!("Creating backup before writing...");
|
||||||
|
@ -57,6 +65,7 @@ impl ResourceList {
|
||||||
self.save_to_writer(file)
|
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> {
|
pub fn save_to_writer(&self, writer: impl std::io::Write) -> Result<(), ResourceSaveError> {
|
||||||
trace!("{self:?}");
|
trace!("{self:?}");
|
||||||
let writer = std::io::BufWriter::new(writer);
|
let writer = std::io::BufWriter::new(writer);
|
||||||
|
@ -64,7 +73,8 @@ impl ResourceList {
|
||||||
Ok(serde_json::to_writer(writer, &self.0)?)
|
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))]
|
#[instrument(level = "debug", skip(self, new_records))]
|
||||||
pub fn merge_records(
|
pub fn merge_records(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -74,6 +84,7 @@ impl ResourceList {
|
||||||
debug!("Building hashset of already taken queries...");
|
debug!("Building hashset of already taken queries...");
|
||||||
let unique_check: HashSet<String> =
|
let unique_check: HashSet<String> =
|
||||||
HashSet::from_iter(self.0.iter().flat_map(Resource::keys).cloned());
|
HashSet::from_iter(self.0.iter().flat_map(Resource::keys).cloned());
|
||||||
|
|
||||||
for record in new_records {
|
for record in new_records {
|
||||||
let record_keys = HashSet::from_iter(record.keys().cloned());
|
let record_keys = HashSet::from_iter(record.keys().cloned());
|
||||||
let collisions = unique_check
|
let collisions = unique_check
|
||||||
|
|
Loading…
Reference in a new issue