Clippy
This commit is contained in:
parent
ce68f4dd42
commit
531a7da636
5 changed files with 57 additions and 53 deletions
|
@ -92,7 +92,7 @@ impl From<&'static str> for Route {
|
|||
/// web path. This incurs a disk check operation, but since users shouldn't be
|
||||
/// updating the config that frequently, it should be fine.
|
||||
impl<'de> Deserialize<'de> for Route {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Route, D::Error>
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
|
@ -233,7 +233,7 @@ pub enum RouteType {
|
|||
Internal,
|
||||
}
|
||||
|
||||
pub struct ConfigData {
|
||||
pub struct FileData {
|
||||
pub path: PathBuf,
|
||||
pub file: File,
|
||||
}
|
||||
|
@ -242,19 +242,19 @@ pub struct ConfigData {
|
|||
/// locations for a place to write a config file to. In order, it checks the
|
||||
/// system-wide config location (`/etc/`, in Linux), followed by the config
|
||||
/// folder, followed by the user's home folder.
|
||||
pub fn get_config_data() -> Result<ConfigData, BunBunError> {
|
||||
pub fn get_config_data() -> Result<FileData, BunBunError> {
|
||||
// Locations to check, with highest priority first
|
||||
let locations: Vec<_> = {
|
||||
let mut folders = vec![PathBuf::from("/etc/")];
|
||||
|
||||
// Config folder
|
||||
if let Some(folder) = config_dir() {
|
||||
folders.push(folder)
|
||||
folders.push(folder);
|
||||
}
|
||||
|
||||
// Home folder
|
||||
if let Some(folder) = home_dir() {
|
||||
folders.push(folder)
|
||||
folders.push(folder);
|
||||
}
|
||||
|
||||
folders
|
||||
|
@ -271,13 +271,13 @@ pub fn get_config_data() -> Result<ConfigData, BunBunError> {
|
|||
match file {
|
||||
Ok(file) => {
|
||||
debug!("Found file at {location:?}.");
|
||||
return Ok(ConfigData {
|
||||
return Ok(FileData {
|
||||
path: location.clone(),
|
||||
file,
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Tried to read '{location:?}' but failed due to error: {e}",)
|
||||
debug!("Tried to read '{location:?}' but failed due to error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ pub fn get_config_data() -> Result<ConfigData, BunBunError> {
|
|||
file.write_all(DEFAULT_CONFIG)?;
|
||||
|
||||
let file = OpenOptions::new().read(true).open(location.clone())?;
|
||||
return Ok(ConfigData {
|
||||
return Ok(FileData {
|
||||
path: location,
|
||||
file,
|
||||
});
|
||||
|
@ -314,19 +314,19 @@ pub fn get_config_data() -> Result<ConfigData, BunBunError> {
|
|||
|
||||
/// Assumes that the user knows what they're talking about and will only try
|
||||
/// to load the config at the given path.
|
||||
pub fn load_custom_path_config(
|
||||
pub fn load_custom_file(
|
||||
path: impl Into<PathBuf>,
|
||||
) -> Result<ConfigData, BunBunError> {
|
||||
) -> Result<FileData, BunBunError> {
|
||||
let path = path.into();
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(&path)
|
||||
.map_err(|e| BunBunError::InvalidConfigPath(path.clone(), e))?;
|
||||
|
||||
Ok(ConfigData { file, path })
|
||||
Ok(FileData { path, file })
|
||||
}
|
||||
|
||||
pub fn read_config(
|
||||
pub fn load_file(
|
||||
mut config_file: File,
|
||||
large_config: bool,
|
||||
) -> Result<Config, BunBunError> {
|
||||
|
@ -417,7 +417,7 @@ mod read_config {
|
|||
fn empty_file() -> Result<()> {
|
||||
let config_file = tempfile::tempfile()?;
|
||||
assert!(matches!(
|
||||
read_config(config_file, false),
|
||||
load_file(config_file, false),
|
||||
Err(BunBunError::ZeroByteConfig)
|
||||
));
|
||||
Ok(())
|
||||
|
@ -428,7 +428,7 @@ mod read_config {
|
|||
let mut config_file = tempfile::tempfile()?;
|
||||
let size_to_write = (LARGE_FILE_SIZE_THRESHOLD + 1) as usize;
|
||||
config_file.write(&[0].repeat(size_to_write))?;
|
||||
match read_config(config_file, false) {
|
||||
match load_file(config_file, false) {
|
||||
Err(BunBunError::ConfigTooLarge(size))
|
||||
if size as usize == size_to_write => {}
|
||||
Err(BunBunError::ConfigTooLarge(size)) => {
|
||||
|
@ -441,7 +441,7 @@ mod read_config {
|
|||
|
||||
#[test]
|
||||
fn valid_config() -> Result<()> {
|
||||
assert!(read_config(File::open("bunbun.default.yaml")?, false).is_ok());
|
||||
assert!(load_file(File::open("bunbun.default.yaml")?, false).is_ok());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::error::Error;
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub enum BunBunError {
|
||||
Io(std::io::Error),
|
||||
Parse(serde_yaml::Error),
|
||||
|
|
43
src/main.rs
43
src/main.rs
|
@ -1,13 +1,13 @@
|
|||
#![forbid(unsafe_code)]
|
||||
#![deny(missing_docs)]
|
||||
#![warn(clippy::nursery, clippy::pedantic)]
|
||||
|
||||
//! Bunbun is a pure-Rust implementation of bunny1 that provides a customizable
|
||||
//! search engine and quick-jump tool in one small binary. For information on
|
||||
//! usage, please take a look at the readme.
|
||||
|
||||
use crate::config::{
|
||||
get_config_data, load_custom_path_config, read_config, ConfigData, Route,
|
||||
RouteGroup,
|
||||
get_config_data, load_custom_file, load_file, FileData, Route, RouteGroup,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use arc_swap::ArcSwap;
|
||||
|
@ -50,15 +50,15 @@ async fn main() -> Result<()> {
|
|||
init_logger(opts.verbose, opts.quiet)?;
|
||||
|
||||
let conf_data = match opts.config {
|
||||
Some(file_name) => load_custom_path_config(file_name),
|
||||
Some(file_name) => load_custom_file(file_name),
|
||||
None => get_config_data(),
|
||||
}?;
|
||||
|
||||
let conf = read_config(conf_data.file.try_clone()?, opts.large_config)?;
|
||||
let conf = load_file(conf_data.file.try_clone()?, opts.large_config)?;
|
||||
let state = Arc::from(ArcSwap::from_pointee(State {
|
||||
public_address: conf.public_address,
|
||||
default_route: conf.default_route,
|
||||
routes: cache_routes(&conf.groups),
|
||||
routes: cache_routes(conf.groups.clone()),
|
||||
groups: conf.groups,
|
||||
}));
|
||||
|
||||
|
@ -106,14 +106,15 @@ fn init_logger(num_verbose_flags: u8, num_quiet_flags: u8) -> Result<()> {
|
|||
/// Generates a hashmap of routes from the data structure created by the config
|
||||
/// file. This should improve runtime performance and is a better solution than
|
||||
/// just iterating over the config object for every hop resolution.
|
||||
fn cache_routes(groups: &[RouteGroup]) -> HashMap<String, Route> {
|
||||
fn cache_routes(groups: Vec<RouteGroup>) -> HashMap<String, Route> {
|
||||
let mut mapping = HashMap::new();
|
||||
for group in groups {
|
||||
for (kw, dest) in &group.routes {
|
||||
for (kw, dest) in group.routes {
|
||||
// This function isn't called often enough to not be a performance issue.
|
||||
match mapping.insert(kw.clone(), dest.clone()) {
|
||||
None => trace!("Inserting {} into mapping.", kw),
|
||||
None => trace!("Inserting {kw} into mapping."),
|
||||
Some(old_value) => {
|
||||
trace!("Overriding {} route from {} to {}.", kw, old_value, dest)
|
||||
trace!("Overriding {kw} route from {old_value} to {dest}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -158,16 +159,14 @@ fn compile_templates() -> Result<Handlebars<'static>> {
|
|||
#[cfg(not(tarpaulin_include))]
|
||||
fn start_watch(
|
||||
state: Arc<ArcSwap<State>>,
|
||||
config_data: ConfigData,
|
||||
config_data: FileData,
|
||||
large_config: bool,
|
||||
) -> Result<Hotwatch> {
|
||||
let mut watch = Hotwatch::new_with_custom_delay(Duration::from_millis(500))?;
|
||||
let ConfigData { path, mut file } = config_data;
|
||||
let FileData { path, mut file } = config_data;
|
||||
let watch_result = watch.watch(&path, move |e: Event| {
|
||||
if let Event::Create(ref path) = e {
|
||||
file = load_custom_path_config(path)
|
||||
.expect("file to exist at path")
|
||||
.file;
|
||||
file = load_custom_file(path).expect("file to exist at path").file;
|
||||
trace!("Getting new file handler as file was recreated.");
|
||||
}
|
||||
|
||||
|
@ -175,7 +174,7 @@ fn start_watch(
|
|||
Event::Write(_) | Event::Create(_) => {
|
||||
trace!("Grabbing writer lock on state...");
|
||||
trace!("Obtained writer lock on state!");
|
||||
match read_config(
|
||||
match load_file(
|
||||
file.try_clone().expect("Failed to clone file handle"),
|
||||
large_config,
|
||||
) {
|
||||
|
@ -183,7 +182,7 @@ fn start_watch(
|
|||
state.store(Arc::new(State {
|
||||
public_address: conf.public_address,
|
||||
default_route: conf.default_route,
|
||||
routes: cache_routes(&conf.groups),
|
||||
routes: cache_routes(conf.groups.clone()),
|
||||
groups: conf.groups,
|
||||
}));
|
||||
info!("Successfully updated active state");
|
||||
|
@ -198,7 +197,9 @@ fn start_watch(
|
|||
match watch_result {
|
||||
Ok(_) => info!("Watcher is now watching {path:?}"),
|
||||
Err(e) => {
|
||||
warn!("Couldn't watch {path:?}: {e}. Changes to this file won't be seen!",)
|
||||
warn!(
|
||||
"Couldn't watch {path:?}: {e}. Changes to this file won't be seen!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,7 +256,7 @@ mod cache_routes {
|
|||
|
||||
#[test]
|
||||
fn empty_groups_yield_empty_routes() {
|
||||
assert_eq!(cache_routes(&[]), HashMap::new());
|
||||
assert_eq!(cache_routes(Vec::new()), HashMap::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -275,7 +276,7 @@ mod cache_routes {
|
|||
};
|
||||
|
||||
assert_eq!(
|
||||
cache_routes(&[group1, group2]),
|
||||
cache_routes(vec![group1, group2]),
|
||||
generate_external_routes(&[
|
||||
("a", "b"),
|
||||
("c", "d"),
|
||||
|
@ -302,7 +303,7 @@ mod cache_routes {
|
|||
};
|
||||
|
||||
assert_eq!(
|
||||
cache_routes(&[group1.clone(), group2]),
|
||||
cache_routes(vec![group1.clone(), group2]),
|
||||
generate_external_routes(&[("a", "1"), ("c", "2")])
|
||||
);
|
||||
|
||||
|
@ -314,7 +315,7 @@ mod cache_routes {
|
|||
};
|
||||
|
||||
assert_eq!(
|
||||
cache_routes(&[group1, group3]),
|
||||
cache_routes(vec![group1, group3]),
|
||||
generate_external_routes(&[("a", "1"), ("b", "2"), ("c", "d")])
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ use log::{debug, error};
|
|||
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// https://url.spec.whatwg.org/#fragment-percent-encode-set
|
||||
// https://url.spec.whatwg.org/#fragment-percent-encode-set
|
||||
const FRAGMENT_ENCODE_SET: &AsciiSet = &CONTROLS
|
||||
.add(b' ')
|
||||
.add(b'"')
|
||||
|
@ -27,6 +27,7 @@ const FRAGMENT_ENCODE_SET: &AsciiSet = &CONTROLS
|
|||
.add(b'#') // Interpreted as a hyperlink section target
|
||||
.add(b'\'');
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
pub async fn index(
|
||||
Extension(data): Extension<Arc<ArcSwap<State>>>,
|
||||
Extension(handlebars): Extension<Handlebars<'static>>,
|
||||
|
@ -40,6 +41,7 @@ pub async fn index(
|
|||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
pub async fn opensearch(
|
||||
Extension(data): Extension<Arc<ArcSwap<State>>>,
|
||||
Extension(handlebars): Extension<Handlebars<'static>>,
|
||||
|
@ -62,6 +64,7 @@ pub async fn opensearch(
|
|||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
pub async fn list(
|
||||
Extension(data): Extension<Arc<ArcSwap<State>>>,
|
||||
Extension(handlebars): Extension<Handlebars<'static>>,
|
||||
|
@ -77,6 +80,7 @@ pub struct SearchQuery {
|
|||
to: String,
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
pub async fn hop(
|
||||
Extension(data): Extension<Arc<ArcSwap<State>>>,
|
||||
Extension(handlebars): Extension<Handlebars<'static>>,
|
||||
|
@ -91,7 +95,7 @@ pub async fn hop(
|
|||
route_type: RouteType::Internal,
|
||||
path,
|
||||
..
|
||||
} => resolve_path(PathBuf::from(path), &args),
|
||||
} => resolve_path(Path::new(path), &args),
|
||||
ConfigRoute {
|
||||
route_type: RouteType::External,
|
||||
path,
|
||||
|
@ -190,7 +194,7 @@ fn resolve_hop<'a>(
|
|||
|
||||
/// Checks if the user provided string has the correct properties required by
|
||||
/// the route to be successfully matched.
|
||||
fn check_route(route: &Route, arg_count: usize) -> bool {
|
||||
const fn check_route(route: &Route, arg_count: usize) -> bool {
|
||||
if let Some(min_args) = route.min_args {
|
||||
if arg_count < min_args {
|
||||
return false;
|
||||
|
@ -217,7 +221,7 @@ enum HopAction {
|
|||
/// so long as the executable was successfully executed. Returns an Error if the
|
||||
/// file doesn't exist or bunbun did not have permission to read and execute the
|
||||
/// file.
|
||||
fn resolve_path(path: PathBuf, args: &str) -> Result<HopAction, BunBunError> {
|
||||
fn resolve_path(path: &Path, args: &str) -> Result<HopAction, BunBunError> {
|
||||
let output = Command::new(path.canonicalize()?)
|
||||
.args(args.split(' '))
|
||||
.output()?;
|
||||
|
@ -369,18 +373,16 @@ mod resolve_path {
|
|||
use super::{resolve_path, HopAction};
|
||||
use anyhow::Result;
|
||||
use std::env::current_dir;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[test]
|
||||
fn invalid_path_returns_err() {
|
||||
assert!(resolve_path(PathBuf::from("/bin/aaaa"), "aaaa").is_err());
|
||||
assert!(resolve_path(&Path::new("/bin/aaaa"), "aaaa").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_path_returns_ok() {
|
||||
assert!(
|
||||
resolve_path(PathBuf::from("/bin/echo"), r#"{"body": "a"}"#).is_ok()
|
||||
);
|
||||
assert!(resolve_path(&Path::new("/bin/echo"), r#"{"body": "a"}"#).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -389,7 +391,7 @@ mod resolve_path {
|
|||
let nest_level = current_dir()?.ancestors().count() - 1;
|
||||
let mut rel_path = PathBuf::from("../".repeat(nest_level));
|
||||
rel_path.push("./bin/echo");
|
||||
assert!(resolve_path(rel_path, r#"{"body": "a"}"#).is_ok());
|
||||
assert!(resolve_path(&rel_path, r#"{"body": "a"}"#).is_ok());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -399,7 +401,7 @@ mod resolve_path {
|
|||
// Trying to run a command without permission
|
||||
format!(
|
||||
"{}",
|
||||
resolve_path(PathBuf::from("/root/some_exec"), "").unwrap_err()
|
||||
resolve_path(&Path::new("/root/some_exec"), "").unwrap_err()
|
||||
)
|
||||
.contains("Permission denied")
|
||||
);
|
||||
|
@ -408,13 +410,13 @@ mod resolve_path {
|
|||
#[test]
|
||||
fn non_success_exit_code_yields_err() {
|
||||
// cat-ing a folder always returns exit code 1
|
||||
assert!(resolve_path(PathBuf::from("/bin/cat"), "/").is_err());
|
||||
assert!(resolve_path(&Path::new("/bin/cat"), "/").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_body() -> Result<()> {
|
||||
assert_eq!(
|
||||
resolve_path(PathBuf::from("/bin/echo"), r#"{"body": "a"}"#)?,
|
||||
resolve_path(&Path::new("/bin/echo"), r#"{"body": "a"}"#)?,
|
||||
HopAction::Body("a".to_string())
|
||||
);
|
||||
|
||||
|
@ -424,7 +426,7 @@ mod resolve_path {
|
|||
#[test]
|
||||
fn return_redirect() -> Result<()> {
|
||||
assert_eq!(
|
||||
resolve_path(PathBuf::from("/bin/echo"), r#"{"redirect": "a"}"#)?,
|
||||
resolve_path(&Path::new("/bin/echo"), r#"{"redirect": "a"}"#)?,
|
||||
HopAction::Redirect("a".to_string())
|
||||
);
|
||||
Ok(())
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::borrow::Cow;
|
|||
use percent_encoding::PercentEncode;
|
||||
use serde::Serialize;
|
||||
|
||||
pub fn query<'a>(query: PercentEncode<'a>) -> impl Serialize + 'a {
|
||||
pub fn query(query: PercentEncode<'_>) -> impl Serialize + '_ {
|
||||
#[derive(Serialize)]
|
||||
struct TemplateArgs<'a> {
|
||||
query: Cow<'a, str>,
|
||||
|
@ -13,7 +13,7 @@ pub fn query<'a>(query: PercentEncode<'a>) -> impl Serialize + 'a {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn hostname<'a>(hostname: &'a str) -> impl Serialize + 'a {
|
||||
pub fn hostname(hostname: &'_ str) -> impl Serialize + '_ {
|
||||
#[derive(Serialize)]
|
||||
pub struct TemplateArgs<'a> {
|
||||
pub hostname: &'a str,
|
||||
|
|
Loading…
Reference in a new issue