diff --git a/bunbun.default.yaml b/bunbun.default.yaml index b63ac8e..3649e16 100644 --- a/bunbun.default.yaml +++ b/bunbun.default.yaml @@ -6,19 +6,30 @@ bind_address: "127.0.0.1:8080" public_address: "localhost:8080" # A default route, if no route is was matched. If none were matched, the entire -# query is used as the query for the default route. This field is optional. +# query is used as the query for the default route. This field is optional, but +# highly recommended for ease-of-use. default_route: "g" -routes: - +# A list containing route groups. Each route group must have a name and a +# mapping of routes, with an optional description field. Each route mapping may +# contain "{{query}}", which will be populated by the user's search query. This +# input is percent-escaped. If multiple routes are defined, then the later +# defined route is used. groups: - meta: - - # Meta - ls: "/ls" - help: "/ls" - list: "/ls" - # Google - g: "https://google.com/search?q={{query}}" - yt: "https://www.youtube.com/results?search_query={{query}}" - r: "https://reddit.com/r/{{query}}" + - + name: "Meta commands" + description: "Commands for bunbun" + routes: + ls: &ls "/ls" + help: *ls + list: *ls + - + name: "Google" + routes: + g: "https://google.com/search?q={{query}}" + yt: "https://www.youtube.com/results?search_query={{query}}" + - + name: "Uncategorized routes" + description: "One-off routes with no specific grouping" + routes: + r: "https://reddit.com/r/{{query}}" diff --git a/src/cli.yaml b/src/cli.yaml index 45cda6b..f4b303e 100644 --- a/src/cli.yaml +++ b/src/cli.yaml @@ -21,5 +21,5 @@ args: - config: short: "c" long: "config" - default_value: "/etc/bunbun.toml" + default_value: "/etc/bunbun.yaml" help: Specify the location of the config file to read from. Needs read/write permissions. diff --git a/src/main.rs b/src/main.rs index c423e38..8ceb75d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use handlebars::Handlebars; use hotwatch::{Event, Hotwatch}; use libc::daemon; use log::{debug, error, info, trace, warn}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::cmp::min; use std::collections::HashMap; use std::fmt; @@ -61,6 +61,8 @@ from_error!(log::SetLoggerError, LoggerInitError); pub struct State { public_address: String, default_route: Option, + groups: Vec, + /// Cached, flattened mapping of all routes and their destinations. routes: HashMap, renderer: Handlebars, } @@ -84,7 +86,8 @@ fn main() -> Result<(), BunBunError> { let state = Arc::from(RwLock::new(State { public_address: conf.public_address, default_route: conf.default_route, - routes: conf.routes, + routes: cache_routes(&conf.groups), + groups: conf.groups, renderer, })); @@ -111,7 +114,8 @@ fn main() -> Result<(), BunBunError> { Ok(conf) => { state.public_address = conf.public_address; state.default_route = conf.default_route; - state.routes = conf.routes; + state.routes = cache_routes(&conf.groups); + state.groups = conf.groups; info!("Successfully updated active state"); } Err(e) => warn!("Failed to update config file: {}", e), @@ -144,6 +148,9 @@ fn main() -> Result<(), BunBunError> { Ok(()) } +/// Initializes the logger based on the number of quiet and verbose flags passed +/// in. Usually, these values are mutually exclusive, that is, if the number of +/// verbose flags is non-zero then the quiet flag is zero, and vice versa. fn init_logger( num_verbose_flags: u64, num_quiet_flags: u64, @@ -171,6 +178,13 @@ struct Config { bind_address: String, public_address: String, default_route: Option, + groups: Vec, +} + +#[derive(Deserialize, Serialize)] +struct RouteGroup { + name: String, + description: Option, routes: HashMap, } @@ -210,6 +224,21 @@ fn read_config(config_file_path: &str) -> Result { Ok(serde_yaml::from_str(&config_str)?) } +fn cache_routes(groups: &[RouteGroup]) -> HashMap { + let mut mapping = HashMap::new(); + for group in groups { + for (kw, dest) in &group.routes { + match mapping.insert(kw.clone(), dest.clone()) { + None => trace!("Inserting {} into mapping.", kw), + Some(old_value) => { + debug!("Overriding {} route from {} to {}.", kw, old_value, dest) + } + } + } + } + mapping +} + /// Returns an instance with all pre-generated templates included into the /// binary. This allows for users to have a portable binary without needed the /// templates at runtime. diff --git a/src/routes.rs b/src/routes.rs index 40b7fb3..0e7d23c 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -23,7 +23,7 @@ const FRAGMENT_ENCODE_SET: &AsciiSet = &CONTROLS #[get("/ls")] pub fn list(data: Data>>) -> impl Responder { let data = data.read().unwrap(); - HttpResponse::Ok().body(data.renderer.render("list", &data.routes).unwrap()) + HttpResponse::Ok().body(data.renderer.render("list", &data.groups).unwrap()) } #[derive(Deserialize)] diff --git a/src/templates/list.hbs b/src/templates/list.hbs index 2e83f03..5fc7268 100644 --- a/src/templates/list.hbs +++ b/src/templates/list.hbs @@ -14,20 +14,29 @@ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } h1, p { margin: 0; } - table { margin-top: 1em; } - td, th { padding: 0 0.5em; } + table { margin-bottom: 1rem; } + header { display: flex; flex-wrap: wrap; align-items: baseline; margin-top: 2rem; } + header h2 { margin: 0 1rem 0 0; } + i { color: rgba(255, 255, 255, 0.5); } + td, th { padding: 0 0.5rem; } .shortcut { text-align: right; } + .target { text-align: left; width: 100%; }

Bunbun Command List

-

To edit this list, edit your bunbun.toml file.

- - - - - - {{#each this}}{{/each}} -
ShortcutTarget
{{@key}}{{this}}
+

To edit this list, edit your bunbun.toml file.

+
+ {{#each this}} {{!-- Iterate over RouteGroup --}} +

{{this.name}}

{{this.description}}
+ + + + + + {{#each this.routes}}{{/each}} +
ShortcutTarget
{{@key}}{{this}}
+ {{/each}} +