Skip to content

Commit

Permalink
refactor(rust): cleanup path handling
Browse files Browse the repository at this point in the history
- Avoids unnecessary string allocation when tracing paths
- Replaces `mut path & path.push()` with `path.join()`
- Avoids unncessary cloning of paths where applicable
- Use `dunce` crate to remove `UNC` prefix
- Improve performance of resolving `~` by avoiding unnecessary string allocations
- Resolve `~`, `$Env:USERPROFILE` and `$HOME` consistenly between different code paths
- Use `PathBuf` instead of `String` for paths in CLI args

I may have missed a couple of places but I think I covered 90% of path handling in the codebase
  • Loading branch information
amrbashir authored and LGUG2Z committed Nov 26, 2023
1 parent a68f384 commit b39e656
Show file tree
Hide file tree
Showing 18 changed files with 305 additions and 530 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ members = [
[workspace.dependencies]
windows-interface = { version = "0.52" }
windows-implement = { version = "0.52" }
dunce = "1"
dirs = "5"
color-eyre = "0.6"

[workspace.dependencies.windows]
version = "0.52"
Expand Down
4 changes: 3 additions & 1 deletion komorebi-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ edition = "2021"

[dependencies]
clap = { version = "4", features = ["derive"] }
color-eyre = "0.6"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
strum = { version = "0.25", features = ["derive"] }
schemars = "0.8"
color-eyre = { workspace = true }
windows = { workspace = true }
dunce = { workspace = true }
dirs = { workspace = true }
24 changes: 11 additions & 13 deletions komorebi-core/src/custom_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use std::fs::File;
use std::io::BufReader;
use std::ops::Deref;
use std::ops::DerefMut;
use std::path::PathBuf;
use std::path::Path;

use color_eyre::eyre::anyhow;
use color_eyre::eyre::bail;
use color_eyre::Result;
use schemars::JsonSchema;
use serde::Deserialize;
Expand All @@ -31,23 +32,20 @@ impl DerefMut for CustomLayout {
}

impl CustomLayout {
pub fn from_path_buf(path: PathBuf) -> Result<Self> {
let invalid_filetype = anyhow!("custom layouts must be json or yaml files");
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
let layout: Self = match path.extension() {
Some(extension) => {
if extension == "yaml" || extension == "yml" {
serde_yaml::from_reader(BufReader::new(File::open(path)?))?
} else if extension == "json" {
serde_json::from_reader(BufReader::new(File::open(path)?))?
} else {
return Err(invalid_filetype);
}
Some(extension) if extension == "yaml" || extension == "yml" => {
serde_json::from_reader(BufReader::new(File::open(path)?))?
}
Some(extension) if extension == "json" => {
serde_json::from_reader(BufReader::new(File::open(path)?))?
}
None => return Err(invalid_filetype),
_ => return Err(anyhow!("custom layouts must be json or yaml files")),
};

if !layout.is_valid() {
return Err(anyhow!("the layout file provided was invalid"));
bail!("the layout file provided was invalid");
}

Ok(layout)
Expand Down
35 changes: 35 additions & 0 deletions komorebi-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#![warn(clippy::all, clippy::nursery, clippy::pedantic)]
#![allow(clippy::missing_errors_doc, clippy::use_self)]

use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;

use clap::ValueEnum;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
use schemars::JsonSchema;
use serde::Deserialize;
Expand Down Expand Up @@ -298,3 +300,36 @@ impl Sizing {
}
}
}

pub fn resolve_home_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
let mut resolved_path = PathBuf::new();
let mut resolved = false;
for c in path.as_ref().components() {
match c {
std::path::Component::Normal(c)
if (c == "~" || c == "$Env:USERPROFILE" || c == "$HOME") && !resolved =>
{
let home = dirs::home_dir().ok_or_else(|| anyhow!("there is no home directory"))?;

resolved_path.extend(home.components());
resolved = true;
}

_ => resolved_path.push(c),
}
}

let parent = resolved_path
.parent()
.ok_or_else(|| anyhow!("cannot parse parent directory"))?;

Ok(if parent.is_dir() {
let file = resolved_path
.components()
.last()
.ok_or_else(|| anyhow!("cannot parse filename"))?;
dunce::canonicalize(parent)?.join(file)
} else {
resolved_path
})
}
4 changes: 2 additions & 2 deletions komorebi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ komorebi-core = { path = "../komorebi-core" }

bitflags = "2"
clap = { version = "4", features = ["derive"] }
color-eyre = "0.6"
crossbeam-channel = "0.5"
crossbeam-utils = "0.8"
ctrlc = "3"
dirs = "5"
getset = "0.1"
hotwatch = "0.4"
lazy_static = "1"
Expand All @@ -45,6 +43,8 @@ winreg = "0.52"
windows-interface = { workspace = true }
windows-implement = { workspace = true }
windows = { workspace = true }
color-eyre = { workspace = true }
dirs = { workspace = true }

[features]
deadlock_detection = []
36 changes: 8 additions & 28 deletions komorebi/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use std::sync::Arc;
use std::time::Duration;

use clap::Parser;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
Expand Down Expand Up @@ -252,7 +251,7 @@ fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
std::env::set_var("RUST_LOG", "info");
}

let appender = tracing_appender::rolling::never(DATA_DIR.clone(), "komorebi.log");
let appender = tracing_appender::rolling::never(&*DATA_DIR, "komorebi.log");
let color_appender = tracing_appender::rolling::never(std::env::temp_dir(), "komorebi.log");
let (non_blocking, guard) = tracing_appender::non_blocking(appender);
let (color_non_blocking, color_guard) = tracing_appender::non_blocking(color_appender);
Expand Down Expand Up @@ -305,13 +304,8 @@ fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
}

pub fn load_configuration() -> Result<()> {
let home = HOME_DIR.clone();

let mut config_pwsh = home.clone();
config_pwsh.push("komorebi.ps1");

let mut config_ahk = home;
config_ahk.push("komorebi.ahk");
let config_pwsh = HOME_DIR.join("komorebi.ps1");
let config_ahk = HOME_DIR.join("komorebi.ahk");

if config_pwsh.exists() {
let powershell_exe = if which("pwsh.exe").is_ok() {
Expand All @@ -320,25 +314,13 @@ pub fn load_configuration() -> Result<()> {
"powershell.exe"
};

tracing::info!(
"loading configuration file: {}",
config_pwsh
.as_os_str()
.to_str()
.ok_or_else(|| anyhow!("cannot convert path to string"))?
);
tracing::info!("loading configuration file: {}", config_pwsh.display());

Command::new(powershell_exe)
.arg(config_pwsh.as_os_str())
.output()?;
} else if config_ahk.exists() && which(&*AHK_EXE).is_ok() {
tracing::info!(
"loading configuration file: {}",
config_ahk
.as_os_str()
.to_str()
.ok_or_else(|| anyhow!("cannot convert path to string"))?
);
tracing::info!("loading configuration file: {}", config_ahk.display());

Command::new(&*AHK_EXE)
.arg(config_ahk.as_os_str())
Expand Down Expand Up @@ -410,7 +392,7 @@ pub fn notify_subscribers(notification: &str) -> Result<()> {
let mut subscriptions = SUBSCRIPTION_PIPES.lock();
for (subscriber, pipe) in &mut *subscriptions {
match writeln!(pipe, "{notification}") {
Ok(_) => {
Ok(()) => {
tracing::debug!("pushed notification to subscriber: {}", subscriber);
}
Err(error) => {
Expand Down Expand Up @@ -528,7 +510,7 @@ fn main() -> Result<()> {
let wm = if let Some(config) = &opts.config {
tracing::info!(
"creating window manager from static configuration file: {}",
config.as_os_str().to_str().unwrap()
config.display()
);

Arc::new(Mutex::new(StaticConfig::preload(
Expand Down Expand Up @@ -557,9 +539,7 @@ fn main() -> Result<()> {
}

if opts.config.is_none() {
std::thread::spawn(|| {
load_configuration().expect("could not load configuration");
});
std::thread::spawn(|| load_configuration().expect("could not load configuration"));

if opts.await_configuration {
let backoff = Backoff::new();
Expand Down
5 changes: 2 additions & 3 deletions komorebi/src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::collections::VecDeque;

use color_eyre::eyre::anyhow;
use color_eyre::eyre::bail;
use color_eyre::Result;
use getset::CopyGetters;
use getset::Getters;
Expand Down Expand Up @@ -120,9 +121,7 @@ impl Monitor {
.ok_or_else(|| anyhow!("there is no workspace"))?;

if workspace.maximized_window().is_some() {
return Err(anyhow!(
"cannot move native maximized window to another monitor or workspace"
));
bail!("cannot move native maximized window to another monitor or workspace");
}

let container = workspace
Expand Down
Loading

0 comments on commit b39e656

Please sign in to comment.