refactor(rust): cleanup path handling

- 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
This commit is contained in:
amrbashir
2023-11-25 09:14:54 +02:00
committed by LGUG2Z
parent a68f3843b7
commit b39e65642b
18 changed files with 305 additions and 530 deletions
Generated
+9
View File
@@ -328,6 +328,12 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "dunce"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
[[package]] [[package]]
name = "dyn-clone" name = "dyn-clone"
version = "1.0.16" version = "1.0.16"
@@ -824,6 +830,8 @@ version = "0.1.19"
dependencies = [ dependencies = [
"clap", "clap",
"color-eyre", "color-eyre",
"dirs",
"dunce",
"schemars", "schemars",
"serde", "serde",
"serde_json", "serde_json",
@@ -840,6 +848,7 @@ dependencies = [
"color-eyre", "color-eyre",
"derive-ahk", "derive-ahk",
"dirs", "dirs",
"dunce",
"fs-tail", "fs-tail",
"heck", "heck",
"komorebi-core", "komorebi-core",
+3
View File
@@ -11,6 +11,9 @@ members = [
[workspace.dependencies] [workspace.dependencies]
windows-interface = { version = "0.52" } windows-interface = { version = "0.52" }
windows-implement = { version = "0.52" } windows-implement = { version = "0.52" }
dunce = "1"
dirs = "5"
color-eyre = "0.6"
[workspace.dependencies.windows] [workspace.dependencies.windows]
version = "0.52" version = "0.52"
+3 -1
View File
@@ -7,10 +7,12 @@ edition = "2021"
[dependencies] [dependencies]
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
color-eyre = "0.6"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
serde_yaml = "0.9" serde_yaml = "0.9"
strum = { version = "0.25", features = ["derive"] } strum = { version = "0.25", features = ["derive"] }
schemars = "0.8" schemars = "0.8"
color-eyre = { workspace = true }
windows = { workspace = true } windows = { workspace = true }
dunce = { workspace = true }
dirs = { workspace = true }
+11 -13
View File
@@ -3,9 +3,10 @@ use std::fs::File;
use std::io::BufReader; use std::io::BufReader;
use std::ops::Deref; use std::ops::Deref;
use std::ops::DerefMut; use std::ops::DerefMut;
use std::path::PathBuf; use std::path::Path;
use color_eyre::eyre::anyhow; use color_eyre::eyre::anyhow;
use color_eyre::eyre::bail;
use color_eyre::Result; use color_eyre::Result;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Deserialize; use serde::Deserialize;
@@ -31,23 +32,20 @@ impl DerefMut for CustomLayout {
} }
impl CustomLayout { impl CustomLayout {
pub fn from_path_buf(path: PathBuf) -> Result<Self> { pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
let invalid_filetype = anyhow!("custom layouts must be json or yaml files"); let path = path.as_ref();
let layout: Self = match path.extension() { let layout: Self = match path.extension() {
Some(extension) => { Some(extension) if extension == "yaml" || extension == "yml" => {
if extension == "yaml" || extension == "yml" { serde_json::from_reader(BufReader::new(File::open(path)?))?
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);
}
} }
None => return Err(invalid_filetype), Some(extension) if extension == "json" => {
serde_json::from_reader(BufReader::new(File::open(path)?))?
}
_ => return Err(anyhow!("custom layouts must be json or yaml files")),
}; };
if !layout.is_valid() { if !layout.is_valid() {
return Err(anyhow!("the layout file provided was invalid")); bail!("the layout file provided was invalid");
} }
Ok(layout) Ok(layout)
+35
View File
@@ -1,10 +1,12 @@
#![warn(clippy::all, clippy::nursery, clippy::pedantic)] #![warn(clippy::all, clippy::nursery, clippy::pedantic)]
#![allow(clippy::missing_errors_doc, clippy::use_self)] #![allow(clippy::missing_errors_doc, clippy::use_self)]
use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use clap::ValueEnum; use clap::ValueEnum;
use color_eyre::eyre::anyhow;
use color_eyre::Result; use color_eyre::Result;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Deserialize; use serde::Deserialize;
@@ -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
})
}
+2 -2
View File
@@ -15,11 +15,9 @@ komorebi-core = { path = "../komorebi-core" }
bitflags = "2" bitflags = "2"
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
color-eyre = "0.6"
crossbeam-channel = "0.5" crossbeam-channel = "0.5"
crossbeam-utils = "0.8" crossbeam-utils = "0.8"
ctrlc = "3" ctrlc = "3"
dirs = "5"
getset = "0.1" getset = "0.1"
hotwatch = "0.4" hotwatch = "0.4"
lazy_static = "1" lazy_static = "1"
@@ -45,6 +43,8 @@ winreg = "0.52"
windows-interface = { workspace = true } windows-interface = { workspace = true }
windows-implement = { workspace = true } windows-implement = { workspace = true }
windows = { workspace = true } windows = { workspace = true }
color-eyre = { workspace = true }
dirs = { workspace = true }
[features] [features]
deadlock_detection = [] deadlock_detection = []
+8 -28
View File
@@ -22,7 +22,6 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use clap::Parser; use clap::Parser;
use color_eyre::eyre::anyhow;
use color_eyre::Result; use color_eyre::Result;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
@@ -252,7 +251,7 @@ fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
std::env::set_var("RUST_LOG", "info"); 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 color_appender = tracing_appender::rolling::never(std::env::temp_dir(), "komorebi.log");
let (non_blocking, guard) = tracing_appender::non_blocking(appender); let (non_blocking, guard) = tracing_appender::non_blocking(appender);
let (color_non_blocking, color_guard) = tracing_appender::non_blocking(color_appender); let (color_non_blocking, color_guard) = tracing_appender::non_blocking(color_appender);
@@ -305,13 +304,8 @@ fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
} }
pub fn load_configuration() -> Result<()> { pub fn load_configuration() -> Result<()> {
let home = HOME_DIR.clone(); let config_pwsh = HOME_DIR.join("komorebi.ps1");
let config_ahk = HOME_DIR.join("komorebi.ahk");
let mut config_pwsh = home.clone();
config_pwsh.push("komorebi.ps1");
let mut config_ahk = home;
config_ahk.push("komorebi.ahk");
if config_pwsh.exists() { if config_pwsh.exists() {
let powershell_exe = if which("pwsh.exe").is_ok() { let powershell_exe = if which("pwsh.exe").is_ok() {
@@ -320,25 +314,13 @@ pub fn load_configuration() -> Result<()> {
"powershell.exe" "powershell.exe"
}; };
tracing::info!( tracing::info!("loading configuration file: {}", config_pwsh.display());
"loading configuration file: {}",
config_pwsh
.as_os_str()
.to_str()
.ok_or_else(|| anyhow!("cannot convert path to string"))?
);
Command::new(powershell_exe) Command::new(powershell_exe)
.arg(config_pwsh.as_os_str()) .arg(config_pwsh.as_os_str())
.output()?; .output()?;
} else if config_ahk.exists() && which(&*AHK_EXE).is_ok() { } else if config_ahk.exists() && which(&*AHK_EXE).is_ok() {
tracing::info!( tracing::info!("loading configuration file: {}", config_ahk.display());
"loading configuration file: {}",
config_ahk
.as_os_str()
.to_str()
.ok_or_else(|| anyhow!("cannot convert path to string"))?
);
Command::new(&*AHK_EXE) Command::new(&*AHK_EXE)
.arg(config_ahk.as_os_str()) .arg(config_ahk.as_os_str())
@@ -410,7 +392,7 @@ pub fn notify_subscribers(notification: &str) -> Result<()> {
let mut subscriptions = SUBSCRIPTION_PIPES.lock(); let mut subscriptions = SUBSCRIPTION_PIPES.lock();
for (subscriber, pipe) in &mut *subscriptions { for (subscriber, pipe) in &mut *subscriptions {
match writeln!(pipe, "{notification}") { match writeln!(pipe, "{notification}") {
Ok(_) => { Ok(()) => {
tracing::debug!("pushed notification to subscriber: {}", subscriber); tracing::debug!("pushed notification to subscriber: {}", subscriber);
} }
Err(error) => { Err(error) => {
@@ -528,7 +510,7 @@ fn main() -> Result<()> {
let wm = if let Some(config) = &opts.config { let wm = if let Some(config) = &opts.config {
tracing::info!( tracing::info!(
"creating window manager from static configuration file: {}", "creating window manager from static configuration file: {}",
config.as_os_str().to_str().unwrap() config.display()
); );
Arc::new(Mutex::new(StaticConfig::preload( Arc::new(Mutex::new(StaticConfig::preload(
@@ -557,9 +539,7 @@ fn main() -> Result<()> {
} }
if opts.config.is_none() { if opts.config.is_none() {
std::thread::spawn(|| { std::thread::spawn(|| load_configuration().expect("could not load configuration"));
load_configuration().expect("could not load configuration");
});
if opts.await_configuration { if opts.await_configuration {
let backoff = Backoff::new(); let backoff = Backoff::new();
+2 -3
View File
@@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::collections::VecDeque; use std::collections::VecDeque;
use color_eyre::eyre::anyhow; use color_eyre::eyre::anyhow;
use color_eyre::eyre::bail;
use color_eyre::Result; use color_eyre::Result;
use getset::CopyGetters; use getset::CopyGetters;
use getset::Getters; use getset::Getters;
@@ -120,9 +121,7 @@ impl Monitor {
.ok_or_else(|| anyhow!("there is no workspace"))?; .ok_or_else(|| anyhow!("there is no workspace"))?;
if workspace.maximized_window().is_some() { if workspace.maximized_window().is_some() {
return Err(anyhow!( bail!("cannot move native maximized window to another monitor or workspace");
"cannot move native maximized window to another monitor or workspace"
));
} }
let container = workspace let container = workspace
+18 -38
View File
@@ -472,10 +472,10 @@ impl WindowManager {
SocketMessage::ChangeLayout(layout) => self.change_workspace_layout_default(layout)?, SocketMessage::ChangeLayout(layout) => self.change_workspace_layout_default(layout)?,
SocketMessage::CycleLayout(direction) => self.cycle_layout(direction)?, SocketMessage::CycleLayout(direction) => self.cycle_layout(direction)?,
SocketMessage::ChangeLayoutCustom(ref path) => { SocketMessage::ChangeLayoutCustom(ref path) => {
self.change_workspace_custom_layout(path.clone())?; self.change_workspace_custom_layout(path)?;
} }
SocketMessage::WorkspaceLayoutCustom(monitor_idx, workspace_idx, ref path) => { SocketMessage::WorkspaceLayoutCustom(monitor_idx, workspace_idx, ref path) => {
self.set_workspace_layout_custom(monitor_idx, workspace_idx, path.clone())?; self.set_workspace_layout_custom(monitor_idx, workspace_idx, path)?;
} }
SocketMessage::WorkspaceTiling(monitor_idx, workspace_idx, tile) => { SocketMessage::WorkspaceTiling(monitor_idx, workspace_idx, tile) => {
self.set_workspace_tiling(monitor_idx, workspace_idx, tile)?; self.set_workspace_tiling(monitor_idx, workspace_idx, tile)?;
@@ -506,7 +506,7 @@ impl WindowManager {
monitor_idx, monitor_idx,
workspace_idx, workspace_idx,
at_container_count, at_container_count,
path.clone(), path,
)?; )?;
} }
SocketMessage::ClearWorkspaceLayoutRules(monitor_idx, workspace_idx) => { SocketMessage::ClearWorkspaceLayoutRules(monitor_idx, workspace_idx) => {
@@ -516,7 +516,7 @@ impl WindowManager {
if let Some((monitor_idx, workspace_idx)) = if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace) self.monitor_workspace_index_by_name(workspace)
{ {
self.set_workspace_layout_custom(monitor_idx, workspace_idx, path.clone())?; self.set_workspace_layout_custom(monitor_idx, workspace_idx, path)?;
} }
} }
SocketMessage::NamedWorkspaceTiling(ref workspace, tile) => { SocketMessage::NamedWorkspaceTiling(ref workspace, tile) => {
@@ -557,7 +557,7 @@ impl WindowManager {
monitor_idx, monitor_idx,
workspace_idx, workspace_idx,
at_container_count, at_container_count,
path.clone(), path,
)?; )?;
} }
} }
@@ -691,10 +691,7 @@ impl WindowManager {
Err(error) => error.to_string(), Err(error) => error.to_string(),
}; };
let mut socket = DATA_DIR.clone(); let socket = DATA_DIR.join("komorebic.sock");
socket.push("komorebic.sock");
let socket = socket.as_path();
let mut stream = UnixStream::connect(socket)?; let mut stream = UnixStream::connect(socket)?;
stream.write_all(state.as_bytes())?; stream.write_all(state.as_bytes())?;
} }
@@ -714,10 +711,7 @@ impl WindowManager {
} }
.to_string(); .to_string();
let mut socket = DATA_DIR.clone(); let socket = DATA_DIR.join("komorebic.sock");
socket.push("komorebic.sock");
let socket = socket.as_path();
let mut stream = UnixStream::connect(socket)?; let mut stream = UnixStream::connect(socket)?;
stream.write_all(response.as_bytes())?; stream.write_all(response.as_bytes())?;
} }
@@ -1034,8 +1028,7 @@ impl WindowManager {
let workspace = self.focused_workspace()?; let workspace = self.focused_workspace()?;
let resize = workspace.resize_dimensions(); let resize = workspace.resize_dimensions();
let mut quicksave_json = std::env::temp_dir(); let quicksave_json = std::env::temp_dir().join("komorebi.quicksave.json");
quicksave_json.push("komorebi.quicksave.json");
let file = OpenOptions::new() let file = OpenOptions::new()
.write(true) .write(true)
@@ -1048,15 +1041,10 @@ impl WindowManager {
SocketMessage::QuickLoad => { SocketMessage::QuickLoad => {
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
let mut quicksave_json = std::env::temp_dir(); let quicksave_json = std::env::temp_dir().join("komorebi.quicksave.json");
quicksave_json.push("komorebi.quicksave.json");
let file = File::open(&quicksave_json).map_err(|_| { let file = File::open(&quicksave_json)
anyhow!( .map_err(|_| anyhow!("no quicksave found at {}", quicksave_json.display()))?;
"no quicksave found at {}",
quicksave_json.display().to_string()
)
})?;
let resize: Vec<Option<Rect>> = serde_json::from_reader(file)?; let resize: Vec<Option<Rect>> = serde_json::from_reader(file)?;
@@ -1071,15 +1059,15 @@ impl WindowManager {
.write(true) .write(true)
.truncate(true) .truncate(true)
.create(true) .create(true)
.open(path.clone())?; .open(path)?;
serde_json::to_writer_pretty(&file, &resize)?; serde_json::to_writer_pretty(&file, &resize)?;
} }
SocketMessage::Load(ref path) => { SocketMessage::Load(ref path) => {
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
let file = File::open(path) let file =
.map_err(|_| anyhow!("no file found at {}", path.display().to_string()))?; File::open(path).map_err(|_| anyhow!("no file found at {}", path.display()))?;
let resize: Vec<Option<Rect>> = serde_json::from_reader(file)?; let resize: Vec<Option<Rect>> = serde_json::from_reader(file)?;
@@ -1191,9 +1179,7 @@ impl WindowManager {
SocketMessage::NotificationSchema => { SocketMessage::NotificationSchema => {
let notification = schema_for!(Notification); let notification = schema_for!(Notification);
let schema = serde_json::to_string_pretty(&notification)?; let schema = serde_json::to_string_pretty(&notification)?;
let mut socket = DATA_DIR.clone(); let socket = DATA_DIR.join("komorebic.sock");
socket.push("komorebic.sock");
let socket = socket.as_path();
let mut stream = UnixStream::connect(socket)?; let mut stream = UnixStream::connect(socket)?;
stream.write_all(schema.as_bytes())?; stream.write_all(schema.as_bytes())?;
@@ -1201,9 +1187,7 @@ impl WindowManager {
SocketMessage::SocketSchema => { SocketMessage::SocketSchema => {
let socket_message = schema_for!(SocketMessage); let socket_message = schema_for!(SocketMessage);
let schema = serde_json::to_string_pretty(&socket_message)?; let schema = serde_json::to_string_pretty(&socket_message)?;
let mut socket = DATA_DIR.clone(); let socket = DATA_DIR.join("komorebic.sock");
socket.push("komorebic.sock");
let socket = socket.as_path();
let mut stream = UnixStream::connect(socket)?; let mut stream = UnixStream::connect(socket)?;
stream.write_all(schema.as_bytes())?; stream.write_all(schema.as_bytes())?;
@@ -1211,18 +1195,14 @@ impl WindowManager {
SocketMessage::StaticConfigSchema => { SocketMessage::StaticConfigSchema => {
let socket_message = schema_for!(StaticConfig); let socket_message = schema_for!(StaticConfig);
let schema = serde_json::to_string_pretty(&socket_message)?; let schema = serde_json::to_string_pretty(&socket_message)?;
let mut socket = DATA_DIR.clone(); let socket = DATA_DIR.join("komorebic.sock");
socket.push("komorebic.sock");
let socket = socket.as_path();
let mut stream = UnixStream::connect(socket)?; let mut stream = UnixStream::connect(socket)?;
stream.write_all(schema.as_bytes())?; stream.write_all(schema.as_bytes())?;
} }
SocketMessage::GenerateStaticConfig => { SocketMessage::GenerateStaticConfig => {
let config = serde_json::to_string_pretty(&StaticConfig::from(&*self))?; let config = serde_json::to_string_pretty(&StaticConfig::from(&*self))?;
let mut socket = DATA_DIR.clone(); let socket = DATA_DIR.join("komorebic.sock");
socket.push("komorebic.sock");
let socket = socket.as_path();
let mut stream = UnixStream::connect(socket)?; let mut stream = UnixStream::connect(socket)?;
stream.write_all(config.as_bytes())?; stream.write_all(config.as_bytes())?;
+1 -1
View File
@@ -31,7 +31,7 @@ pub fn listen_for_movements(wm: Arc<Mutex<WindowManager>>) {
Event::MouseMoveRelative { .. } => { Event::MouseMoveRelative { .. } => {
if !ignore_movement { if !ignore_movement {
match wm.lock().raise_window_at_cursor_pos() { match wm.lock().raise_window_at_cursor_pos() {
Ok(_) => {} Ok(()) => {}
Err(error) => tracing::error!("{}", error), Err(error) => tracing::error!("{}", error),
} }
} }
+4 -9
View File
@@ -29,13 +29,13 @@ use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WORKSPACE_RULES; use crate::WORKSPACE_RULES;
use color_eyre::Result; use color_eyre::Result;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use dirs::home_dir;
use hotwatch::notify::DebouncedEvent; use hotwatch::notify::DebouncedEvent;
use hotwatch::Hotwatch; use hotwatch::Hotwatch;
use komorebi_core::config_generation::ApplicationConfigurationGenerator; use komorebi_core::config_generation::ApplicationConfigurationGenerator;
use komorebi_core::config_generation::ApplicationOptions; use komorebi_core::config_generation::ApplicationOptions;
use komorebi_core::config_generation::IdWithIdentifier; use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingStrategy; use komorebi_core::config_generation::MatchingStrategy;
use komorebi_core::resolve_home_path;
use komorebi_core::ApplicationIdentifier; use komorebi_core::ApplicationIdentifier;
use komorebi_core::DefaultLayout; use komorebi_core::DefaultLayout;
use komorebi_core::FocusFollowsMouseImplementation; use komorebi_core::FocusFollowsMouseImplementation;
@@ -616,13 +616,8 @@ impl StaticConfig {
} }
if let Some(path) = &self.app_specific_configuration_path { if let Some(path) = &self.app_specific_configuration_path {
let stringified = path.to_string_lossy(); let path = resolve_home_path(path)?;
let stringified = stringified.replace( let content = std::fs::read_to_string(path)?;
"$Env:USERPROFILE",
&home_dir().expect("no home dir").to_string_lossy(),
);
let content = std::fs::read_to_string(stringified)?;
let asc = ApplicationConfigurationGenerator::load(&content)?; let asc = ApplicationConfigurationGenerator::load(&content)?;
for mut entry in asc { for mut entry in asc {
@@ -762,7 +757,7 @@ impl StaticConfig {
let socket = DATA_DIR.join("komorebi.sock"); let socket = DATA_DIR.join("komorebi.sock");
match std::fs::remove_file(&socket) { match std::fs::remove_file(&socket) {
Ok(_) => {} Ok(()) => {}
Err(error) => match error.kind() { Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ // Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {} ErrorKind::NotFound => {}
+4 -4
View File
@@ -241,7 +241,7 @@ impl Window {
// Raise Window to foreground // Raise Window to foreground
match WindowsApi::set_foreground_window(self.hwnd()) { match WindowsApi::set_foreground_window(self.hwnd()) {
Ok(_) => {} Ok(()) => {}
Err(error) => { Err(error) => {
tracing::error!( tracing::error!(
"could not set as foreground window, but continuing execution of raise(): {}", "could not set as foreground window, but continuing execution of raise(): {}",
@@ -252,7 +252,7 @@ impl Window {
// This isn't really needed when the above command works as expected via AHK // This isn't really needed when the above command works as expected via AHK
match WindowsApi::set_focus(self.hwnd()) { match WindowsApi::set_focus(self.hwnd()) {
Ok(_) => {} Ok(()) => {}
Err(error) => { Err(error) => {
tracing::error!( tracing::error!(
"could not set focus, but continuing execution of raise(): {}", "could not set focus, but continuing execution of raise(): {}",
@@ -302,7 +302,7 @@ impl Window {
} }
match WindowsApi::set_foreground_window(self.hwnd()) { match WindowsApi::set_foreground_window(self.hwnd()) {
Ok(_) => { Ok(()) => {
foregrounded = true; foregrounded = true;
} }
Err(error) => { Err(error) => {
@@ -334,7 +334,7 @@ impl Window {
// This isn't really needed when the above command works as expected via AHK // This isn't really needed when the above command works as expected via AHK
match WindowsApi::set_focus(self.hwnd()) { match WindowsApi::set_focus(self.hwnd()) {
Ok(_) => {} Ok(()) => {}
Err(error) => { Err(error) => {
tracing::error!( tracing::error!(
"could not set focus, but continuing execution of focus(): {}", "could not set focus, but continuing execution of focus(): {}",
+62 -75
View File
@@ -3,11 +3,13 @@ use std::collections::HashSet;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::Arc; use std::sync::Arc;
use color_eyre::eyre::anyhow; use color_eyre::eyre::anyhow;
use color_eyre::eyre::bail;
use color_eyre::Result; use color_eyre::Result;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use hotwatch::notify::DebouncedEvent; use hotwatch::notify::DebouncedEvent;
@@ -166,7 +168,7 @@ impl WindowManager {
let socket = DATA_DIR.join("komorebi.sock"); let socket = DATA_DIR.join("komorebi.sock");
match std::fs::remove_file(&socket) { match std::fs::remove_file(&socket) {
Ok(_) => {} Ok(()) => {}
Err(error) => match error.kind() { Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ // Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {} ErrorKind::NotFound => {}
@@ -247,13 +249,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn watch_configuration(&mut self, enable: bool) -> Result<()> { pub fn watch_configuration(&mut self, enable: bool) -> Result<()> {
let home = HOME_DIR.clone(); let config_pwsh = HOME_DIR.join("komorebi.ps1");
let config_ahk = HOME_DIR.join("komorebi.ahk");
let mut config_pwsh = home.clone();
config_pwsh.push("komorebi.ps1");
let mut config_ahk = home;
config_ahk.push("komorebi.ahk");
if config_pwsh.exists() { if config_pwsh.exists() {
self.configure_watcher(enable, config_pwsh)?; self.configure_watcher(enable, config_pwsh)?;
@@ -265,50 +262,39 @@ impl WindowManager {
} }
fn configure_watcher(&mut self, enable: bool, config: PathBuf) -> Result<()> { fn configure_watcher(&mut self, enable: bool, config: PathBuf) -> Result<()> {
if config.exists() { if enable {
if enable { tracing::info!("watching configuration for changes: {}", config.display());
tracing::info!( // Always make absolutely sure that there isn't an already existing watch, because
"watching configuration for changes: {}", // hotwatch allows multiple watches to be registered for the same path
config match self.hotwatch.unwatch(&config) {
.as_os_str() Ok(()) => {}
.to_str() Err(error) => match error {
.ok_or_else(|| anyhow!("cannot convert path to string"))? hotwatch::Error::Notify(error) => match error {
); hotwatch::notify::Error::WatchNotFound => {}
// Always make absolutely sure that there isn't an already existing watch, because error => return Err(error.into()),
// hotwatch allows multiple watches to be registered for the same path
match self.hotwatch.unwatch(config.clone()) {
Ok(_) => {}
Err(error) => match error {
hotwatch::Error::Notify(error) => match error {
hotwatch::notify::Error::WatchNotFound => {}
error => return Err(error.into()),
},
error @ hotwatch::Error::Io(_) => return Err(error.into()),
}, },
error @ hotwatch::Error::Io(_) => return Err(error.into()),
},
}
self.hotwatch.watch(config, |event| match event {
// Editing in Notepad sends a NoticeWrite while editing in (Neo)Vim sends
// a NoticeRemove, presumably because of the use of swap files?
DebouncedEvent::NoticeWrite(_) | DebouncedEvent::NoticeRemove(_) => {
std::thread::spawn(|| {
load_configuration().expect("could not load configuration");
});
} }
_ => {}
})?;
} else {
tracing::info!(
"no longer watching configuration for changes: {}",
config.display()
);
self.hotwatch.watch(config, |event| match event { self.hotwatch.unwatch(config)?;
// Editing in Notepad sends a NoticeWrite while editing in (Neo)Vim sends };
// a NoticeRemove, presumably because of the use of swap files?
DebouncedEvent::NoticeWrite(_) | DebouncedEvent::NoticeRemove(_) => {
std::thread::spawn(|| {
load_configuration().expect("could not load configuration");
});
}
_ => {}
})?;
} else {
tracing::info!(
"no longer watching configuration for changes: {}",
config
.as_os_str()
.to_str()
.ok_or_else(|| anyhow!("cannot convert path to string"))?
);
self.hotwatch.unwatch(config)?;
};
}
Ok(()) Ok(())
} }
@@ -868,7 +854,7 @@ impl WindowManager {
// attach to the thread of the desktop window always seems to result in "Access is // attach to the thread of the desktop window always seems to result in "Access is
// denied (os error 5)" // denied (os error 5)"
match WindowsApi::set_foreground_window(desktop_window.hwnd()) { match WindowsApi::set_foreground_window(desktop_window.hwnd()) {
Ok(_) => {} Ok(()) => {}
Err(error) => { Err(error) => {
tracing::warn!("{} {}:{}", error, file!(), line!()); tracing::warn!("{} {}:{}", error, file!(), line!());
} }
@@ -1002,9 +988,7 @@ impl WindowManager {
let workspace = self.focused_workspace()?; let workspace = self.focused_workspace()?;
let focused_hwnd = WindowsApi::foreground_window()?; let focused_hwnd = WindowsApi::foreground_window()?;
if !workspace.contains_managed_window(focused_hwnd) { if !workspace.contains_managed_window(focused_hwnd) {
return Err(anyhow!( bail!("ignoring commands while active window is not managed by komorebi");
"ignoring commands while active window is not managed by komorebi"
));
} }
} }
@@ -1120,9 +1104,7 @@ impl WindowManager {
.ok_or_else(|| anyhow!("there is no workspace"))?; .ok_or_else(|| anyhow!("there is no workspace"))?;
if workspace.maximized_window().is_some() { if workspace.maximized_window().is_some() {
return Err(anyhow!( bail!("cannot move native maximized window to another monitor or workspace");
"cannot move native maximized window to another monitor or workspace"
));
} }
let container = workspace let container = workspace
@@ -1232,9 +1214,7 @@ impl WindowManager {
// removing this messes up the monitor / container / window index somewhere // removing this messes up the monitor / container / window index somewhere
// and results in the wrong window getting moved across the monitor boundary // and results in the wrong window getting moved across the monitor boundary
if workspace.is_focused_window_monocle_or_maximized()? { if workspace.is_focused_window_monocle_or_maximized()? {
return Err(anyhow!( bail!("ignoring command while active window is in monocle mode or maximized");
"ignoring command while active window is in monocle mode or maximized"
));
} }
tracing::info!("moving container"); tracing::info!("moving container");
@@ -1396,9 +1376,7 @@ impl WindowManager {
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
if workspace.is_focused_window_monocle_or_maximized()? { if workspace.is_focused_window_monocle_or_maximized()? {
return Err(anyhow!( bail!("ignoring command while active window is in monocle mode or maximized");
"ignoring command while active window is in monocle mode or maximized"
));
} }
tracing::info!("moving container"); tracing::info!("moving container");
@@ -1425,7 +1403,7 @@ impl WindowManager {
.ok_or_else(|| anyhow!("there must be at least one window in a container"))?; .ok_or_else(|| anyhow!("there must be at least one window in a container"))?;
if len.get() == 1 { if len.get() == 1 {
return Err(anyhow!("there is only one window in this container")); bail!("there is only one window in this container");
} }
let current_idx = container.focused_window_idx(); let current_idx = container.focused_window_idx();
@@ -1510,7 +1488,7 @@ impl WindowManager {
tracing::info!("removing window"); tracing::info!("removing window");
if self.focused_container()?.windows().len() == 1 { if self.focused_container()?.windows().len() == 1 {
return Err(anyhow!("a container must have at least one window")); bail!("a container must have at least one window");
} }
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
@@ -1729,10 +1707,13 @@ impl WindowManager {
} }
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn change_workspace_custom_layout(&mut self, path: PathBuf) -> Result<()> { pub fn change_workspace_custom_layout<P>(&mut self, path: P) -> Result<()>
where
P: AsRef<Path> + std::fmt::Debug,
{
tracing::info!("changing layout"); tracing::info!("changing layout");
let layout = CustomLayout::from_path_buf(path)?; let layout = CustomLayout::from_path(path)?;
let workspace = self.focused_workspace_mut()?; let workspace = self.focused_workspace_mut()?;
match workspace.layout() { match workspace.layout() {
@@ -1854,13 +1835,16 @@ impl WindowManager {
} }
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn add_workspace_layout_custom_rule( pub fn add_workspace_layout_custom_rule<P>(
&mut self, &mut self,
monitor_idx: usize, monitor_idx: usize,
workspace_idx: usize, workspace_idx: usize,
at_container_count: usize, at_container_count: usize,
path: PathBuf, path: P,
) -> Result<()> { ) -> Result<()>
where
P: AsRef<Path> + std::fmt::Debug,
{
tracing::info!("setting workspace layout"); tracing::info!("setting workspace layout");
let invisible_borders = self.invisible_borders; let invisible_borders = self.invisible_borders;
@@ -1885,7 +1869,7 @@ impl WindowManager {
.get_mut(workspace_idx) .get_mut(workspace_idx)
.ok_or_else(|| anyhow!("there is no monitor"))?; .ok_or_else(|| anyhow!("there is no monitor"))?;
let layout = CustomLayout::from_path_buf(path)?; let layout = CustomLayout::from_path(path)?;
let rules: &mut Vec<(usize, Layout)> = workspace.layout_rules_mut(); let rules: &mut Vec<(usize, Layout)> = workspace.layout_rules_mut();
rules.retain(|pair| pair.0 != at_container_count); rules.retain(|pair| pair.0 != at_container_count);
@@ -1986,14 +1970,17 @@ impl WindowManager {
} }
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn set_workspace_layout_custom( pub fn set_workspace_layout_custom<P>(
&mut self, &mut self,
monitor_idx: usize, monitor_idx: usize,
workspace_idx: usize, workspace_idx: usize,
path: PathBuf, path: P,
) -> Result<()> { ) -> Result<()>
where
P: AsRef<Path> + std::fmt::Debug,
{
tracing::info!("setting workspace layout"); tracing::info!("setting workspace layout");
let layout = CustomLayout::from_path_buf(path)?; let layout = CustomLayout::from_path(path)?;
let invisible_borders = self.invisible_borders; let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset; let offset = self.work_area_offset;
let focused_monitor_idx = self.focused_monitor_idx(); let focused_monitor_idx = self.focused_monitor_idx();
@@ -2164,7 +2151,7 @@ impl WindowManager {
if self.monitors().get(idx).is_some() { if self.monitors().get(idx).is_some() {
self.monitors.focus(idx); self.monitors.focus(idx);
} else { } else {
return Err(anyhow!("this is not a valid monitor index")); bail!("this is not a valid monitor index");
} }
Ok(()) Ok(())
+1 -1
View File
@@ -368,7 +368,7 @@ impl WindowsApi {
pub fn close_window(hwnd: HWND) -> Result<()> { pub fn close_window(hwnd: HWND) -> Result<()> {
match Self::post_message(hwnd, WM_CLOSE, WPARAM(0), LPARAM(0)) { match Self::post_message(hwnd, WM_CLOSE, WPARAM(0), LPARAM(0)) {
Ok(_) => Ok(()), Ok(()) => Ok(()),
Err(_) => Err(anyhow!("could not close window")), Err(_) => Err(anyhow!("could not close window")),
} }
} }
+1 -1
View File
@@ -61,7 +61,7 @@ impl WinEventListener {
MessageLoop::start(10, |_msg| { MessageLoop::start(10, |_msg| {
if let Ok(event) = WINEVENT_CALLBACK_CHANNEL.lock().1.try_recv() { if let Ok(event) = WINEVENT_CALLBACK_CHANNEL.lock().1.try_recv() {
match outgoing.send(event) { match outgoing.send(event) {
Ok(_) => {} Ok(()) => {}
Err(error) => { Err(error) => {
tracing::error!("{}", error); tracing::error!("{}", error);
} }
+2 -2
View File
@@ -108,7 +108,7 @@ impl Workspace {
} }
if let Some(pathbuf) = &config.custom_layout { if let Some(pathbuf) = &config.custom_layout {
let layout = CustomLayout::from_path_buf(pathbuf.clone())?; let layout = CustomLayout::from_path(pathbuf)?;
self.layout = Layout::Custom(layout); self.layout = Layout::Custom(layout);
self.tile = true; self.tile = true;
} }
@@ -129,7 +129,7 @@ impl Workspace {
if let Some(layout_rules) = &config.custom_layout_rules { if let Some(layout_rules) = &config.custom_layout_rules {
let rules = self.layout_rules_mut(); let rules = self.layout_rules_mut();
for (count, pathbuf) in layout_rules { for (count, pathbuf) in layout_rules {
let rule = CustomLayout::from_path_buf(pathbuf.clone())?; let rule = CustomLayout::from_path(pathbuf)?;
rules.push((*count, Layout::Custom(rule))); rules.push((*count, Layout::Custom(rule)));
} }
} }
+3 -2
View File
@@ -15,8 +15,6 @@ derive-ahk = { path = "../derive-ahk" }
komorebi-core = { path = "../komorebi-core" } komorebi-core = { path = "../komorebi-core" }
clap = { version = "4", features = ["derive", "wrap_help"] } clap = { version = "4", features = ["derive", "wrap_help"] }
color-eyre = "0.6"
dirs = "5"
fs-tail = "0.1" fs-tail = "0.1"
heck = "0.4" heck = "0.4"
lazy_static = "1" lazy_static = "1"
@@ -30,3 +28,6 @@ sysinfo = "0.29"
uds_windows = "1" uds_windows = "1"
which = "5" which = "5"
windows = { workspace = true } windows = { workspace = true }
color-eyre = { workspace = true }
dirs = { workspace = true }
dunce = { workspace = true }
+136 -350
View File
@@ -16,9 +16,11 @@ use std::time::Duration;
use clap::Parser; use clap::Parser;
use clap::ValueEnum; use clap::ValueEnum;
use color_eyre::eyre::anyhow; use color_eyre::eyre::anyhow;
use color_eyre::eyre::bail;
use color_eyre::Result; use color_eyre::Result;
use fs_tail::TailedFile; use fs_tail::TailedFile;
use heck::ToKebabCase; use heck::ToKebabCase;
use komorebi_core::resolve_home_path;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use paste::paste; use paste::paste;
use sysinfo::SystemExt; use sysinfo::SystemExt;
@@ -259,7 +261,7 @@ pub struct WorkspaceCustomLayout {
workspace: usize, workspace: usize,
/// JSON or YAML file from which the custom layout definition should be loaded /// JSON or YAML file from which the custom layout definition should be loaded
path: String, path: PathBuf,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
@@ -268,7 +270,7 @@ pub struct NamedWorkspaceCustomLayout {
workspace: String, workspace: String,
/// JSON or YAML file from which the custom layout definition should be loaded /// JSON or YAML file from which the custom layout definition should be loaded
path: String, path: PathBuf,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
@@ -310,7 +312,7 @@ pub struct WorkspaceCustomLayoutRule {
at_container_count: usize, at_container_count: usize,
/// JSON or YAML file from which the custom layout definition should be loaded /// JSON or YAML file from which the custom layout definition should be loaded
path: String, path: PathBuf,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
@@ -322,7 +324,7 @@ pub struct NamedWorkspaceCustomLayoutRule {
at_container_count: usize, at_container_count: usize,
/// JSON or YAML file from which the custom layout definition should be loaded /// JSON or YAML file from which the custom layout definition should be loaded
path: String, path: PathBuf,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
@@ -658,19 +660,19 @@ struct Stop {
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
struct SaveResize { struct SaveResize {
/// File to which the resize layout dimensions should be saved /// File to which the resize layout dimensions should be saved
path: String, path: PathBuf,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
struct LoadResize { struct LoadResize {
/// File from which the resize layout dimensions should be loaded /// File from which the resize layout dimensions should be loaded
path: String, path: PathBuf,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
struct LoadCustomLayout { struct LoadCustomLayout {
/// JSON or YAML file from which the custom layout definition should be loaded /// JSON or YAML file from which the custom layout definition should be loaded
path: String, path: PathBuf,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
@@ -688,23 +690,23 @@ struct Unsubscribe {
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
struct AhkAppSpecificConfiguration { struct AhkAppSpecificConfiguration {
/// YAML file from which the application-specific configurations should be loaded /// YAML file from which the application-specific configurations should be loaded
path: String, path: PathBuf,
/// Optional YAML file of overrides to apply over the first file /// Optional YAML file of overrides to apply over the first file
override_path: Option<String>, override_path: Option<PathBuf>,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
struct PwshAppSpecificConfiguration { struct PwshAppSpecificConfiguration {
/// YAML file from which the application-specific configurations should be loaded /// YAML file from which the application-specific configurations should be loaded
path: String, path: PathBuf,
/// Optional YAML file of overrides to apply over the first file /// Optional YAML file of overrides to apply over the first file
override_path: Option<String>, override_path: Option<PathBuf>,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
struct FormatAppSpecificConfiguration { struct FormatAppSpecificConfiguration {
/// YAML file from which the application-specific configurations should be loaded /// YAML file from which the application-specific configurations should be loaded
path: String, path: PathBuf,
} }
#[derive(Parser, AhkFunction)] #[derive(Parser, AhkFunction)]
@@ -717,7 +719,7 @@ struct AltFocusHack {
struct EnableAutostart { struct EnableAutostart {
/// Path to a static configuration JSON file /// Path to a static configuration JSON file
#[clap(action, short, long)] #[clap(action, short, long)]
config: String, config: PathBuf,
/// Enable komorebi's custom focus-follows-mouse implementation /// Enable komorebi's custom focus-follows-mouse implementation
#[clap(action, short, long = "ffm")] #[clap(action, short, long = "ffm")]
ffm: bool, ffm: bool,
@@ -1106,15 +1108,48 @@ pub fn send_message(bytes: &[u8]) -> Result<()> {
Ok(stream.write_all(bytes)?) Ok(stream.write_all(bytes)?)
} }
fn with_komorebic_socket<F: Fn() -> Result<()>>(f: F) -> Result<()> {
let socket = DATA_DIR.join("komorebic.sock");
match std::fs::remove_file(&socket) {
Ok(()) => {}
Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {}
_ => {
return Err(error.into());
}
},
};
f()?;
let listener = UnixListener::bind(socket)?;
match listener.accept() {
Ok(incoming) => {
let stream = BufReader::new(incoming.0);
for line in stream.lines() {
println!("{}", line?);
}
Ok(())
}
Err(error) => {
panic!("{}", error);
}
}
}
fn startup_dir() -> Result<PathBuf> { fn startup_dir() -> Result<PathBuf> {
let home_dir = dirs::home_dir().expect("unable to obtain user's home folder"); let startup = dirs::home_dir()
let app_data = home_dir.join("AppData"); .expect("unable to obtain user's home folder")
let roaming = app_data.join("Roaming"); .join("AppData")
let microsoft = roaming.join("Microsoft"); .join("Roaming")
let windows = microsoft.join("Windows"); .join("Microsoft")
let start_menu = windows.join("Start Menu"); .join("Windows")
let programs = start_menu.join("Programs"); .join("Start Menu")
let startup = programs.join("Startup"); .join("Programs")
.join("Startup");
if !startup.is_dir() { if !startup.is_dir() {
std::fs::create_dir_all(&startup)?; std::fs::create_dir_all(&startup)?;
@@ -1132,33 +1167,25 @@ fn main() -> Result<()> {
let version = env!("CARGO_PKG_VERSION"); let version = env!("CARGO_PKG_VERSION");
let home_dir = dirs::home_dir().expect("could not find home dir"); let home_dir = dirs::home_dir().expect("could not find home dir");
let mut config_dir = home_dir; let config_dir = home_dir.join(".config");
config_dir.push(".config"); std::fs::create_dir_all(&config_dir)?;
std::fs::create_dir_all(".config")?;
let komorebi_json = reqwest::blocking::get( let komorebi_json = reqwest::blocking::get(
format!("https://raw.githubusercontent.com/LGUG2Z/komorebi/v{version}/komorebi.example.json") format!("https://raw.githubusercontent.com/LGUG2Z/komorebi/v{version}/komorebi.example.json")
)?.text()?; )?.text()?;
let mut komorebi_json_file_path = HOME_DIR.clone(); std::fs::write(HOME_DIR.join("komorebi.json"), komorebi_json)?;
komorebi_json_file_path.push("komorebi.json");
std::fs::write(komorebi_json_file_path, komorebi_json)?;
let applications_yaml = reqwest::blocking::get( let applications_yaml = reqwest::blocking::get(
"https://raw.githubusercontent.com/LGUG2Z/komorebi-application-specific-configuration/master/applications.yaml" "https://raw.githubusercontent.com/LGUG2Z/komorebi-application-specific-configuration/master/applications.yaml"
)? )?
.text()?; .text()?;
let mut komorebi_json_file_path = HOME_DIR.clone(); std::fs::write(HOME_DIR.join("applications.yaml"), applications_yaml)?;
komorebi_json_file_path.push("applications.yaml");
std::fs::write(komorebi_json_file_path, applications_yaml)?;
let whkdrc = reqwest::blocking::get(format!( let whkdrc = reqwest::blocking::get(format!(
"https://raw.githubusercontent.com/LGUG2Z/komorebi/v{version}/whkdrc.sample" "https://raw.githubusercontent.com/LGUG2Z/komorebi/v{version}/whkdrc.sample"
))? ))?
.text()?; .text()?;
let mut whkdrc_file_path = config_dir.clone(); std::fs::write(config_dir.join("whkdrc"), whkdrc)?;
whkdrc_file_path.push("whkdrc");
std::fs::write(whkdrc_file_path, whkdrc)?;
println!("Example ~/komorebi.json, ~/.config/whkdrc and latest ~/applications.yaml files downloaded"); println!("Example ~/komorebi.json, ~/.config/whkdrc and latest ~/applications.yaml files downloaded");
println!( println!(
@@ -1166,15 +1193,20 @@ fn main() -> Result<()> {
); );
} }
SubCommand::EnableAutostart(args) => { SubCommand::EnableAutostart(args) => {
let mut current_exe = std::env::current_exe().expect("unable to get exec path"); let mut current_exe_dir = std::env::current_exe().expect("unable to get exec path");
current_exe.pop(); current_exe_dir.pop();
let komorebic_exe = current_exe.join("komorebic.exe"); let komorebic_exe = current_exe_dir.join("komorebic.exe");
let komorebic_exe = dunce::simplified(&komorebic_exe);
let startup_dir = startup_dir()?; let startup_dir = startup_dir()?;
let shortcut_file = startup_dir.join("komorebi.lnk"); let shortcut_file = startup_dir.join("komorebi.lnk");
let shortcut_file = dunce::simplified(&shortcut_file);
let mut arguments = format!("start --config {}", args.config); let mut arguments = format!(
"start --config {}",
dunce::canonicalize(args.config)?.display()
);
if args.ffm { if args.ffm {
arguments.push_str(" --ffm"); arguments.push_str(" --ffm");
@@ -1187,12 +1219,12 @@ fn main() -> Result<()> {
} }
Command::new("powershell") Command::new("powershell")
.arg("-c") .arg("-c")
.arg("$WshShell = New-Object -comObject WScript.Shell; $Shortcut = $WshShell.CreateShortcut($env:SHORTCUT_PATH); $Shortcut.TargetPath = $env:TARGET_PATH; $Shortcut.Arguments = $env:TARGET_ARGS; $Shortcut.Save()") .arg("$WshShell = New-Object -comObject WScript.Shell; $Shortcut = $WshShell.CreateShortcut($env:SHORTCUT_PATH); $Shortcut.TargetPath = $env:TARGET_PATH; $Shortcut.Arguments = $env:TARGET_ARGS; $Shortcut.Save()")
.env("SHORTCUT_PATH", shortcut_file.to_string_lossy().to_string()) .env("SHORTCUT_PATH", shortcut_file.as_os_str())
.env("TARGET_PATH", komorebic_exe.to_string_lossy().to_string()) .env("TARGET_PATH", komorebic_exe.as_os_str())
.env("TARGET_ARGS", arguments) .env("TARGET_ARGS", arguments)
.output()?; .output()?;
} }
SubCommand::DisableAutostart => { SubCommand::DisableAutostart => {
let startup_dir = startup_dir()?; let startup_dir = startup_dir()?;
@@ -1203,10 +1235,9 @@ fn main() -> Result<()> {
} }
} }
SubCommand::Check => { SubCommand::Check => {
let home = HOME_DIR.clone(); let home_display = HOME_DIR.display();
let home_lossy_string = home.to_string_lossy();
if HAS_CUSTOM_CONFIG_HOME.load(Ordering::SeqCst) { if HAS_CUSTOM_CONFIG_HOME.load(Ordering::SeqCst) {
println!("KOMOREBI_CONFIG_HOME detected: {home_lossy_string}\n"); println!("KOMOREBI_CONFIG_HOME detected: {home_display}\n");
} else { } else {
println!( println!(
"No KOMOREBI_CONFIG_HOME detected, defaulting to {}\n", "No KOMOREBI_CONFIG_HOME detected, defaulting to {}\n",
@@ -1216,20 +1247,15 @@ fn main() -> Result<()> {
); );
} }
println!("Looking for configuration files in {home_lossy_string}\n"); println!("Looking for configuration files in {home_display}\n");
let mut static_config = home.clone(); let static_config = HOME_DIR.join("komorebi.json");
static_config.push("komorebi.json"); let config_pwsh = HOME_DIR.join("komorebi.ps1");
let config_ahk = HOME_DIR.join("komorebi.ahk");
let mut config_pwsh = home.clone(); let config_whkd = dirs::home_dir()
config_pwsh.push("komorebi.ps1"); .expect("no home dir found")
.join(".config")
let mut config_ahk = home.clone(); .join("whkdrc");
config_ahk.push("komorebi.ahk");
let mut config_whkd = dirs::home_dir().expect("no home dir found");
config_whkd.push(".config");
config_whkd.push("whkdrc");
if static_config.exists() { if static_config.exists() {
println!("Found komorebi.json; this file can be passed to the start command with the --config flag\n"); println!("Found komorebi.json; this file can be passed to the start command with the --config flag\n");
@@ -1248,13 +1274,12 @@ fn main() -> Result<()> {
} else if config_ahk.exists() { } else if config_ahk.exists() {
println!("Found komorebi.ahk; this file will be autoloaded by komorebi\n"); println!("Found komorebi.ahk; this file will be autoloaded by komorebi\n");
} else { } else {
println!("No komorebi configuration found in {home_lossy_string}\n"); println!("No komorebi configuration found in {home_display}\n");
println!("If running 'komorebic start --await-configuration', you will manually have to call the following command to begin tiling: komorebic complete-configuration\n"); println!("If running 'komorebic start --await-configuration', you will manually have to call the following command to begin tiling: komorebic complete-configuration\n");
} }
} }
SubCommand::AhkLibrary => { SubCommand::AhkLibrary => {
let mut library = HOME_DIR.clone(); let library = HOME_DIR.join("komorebic.lib.ahk");
library.push("komorebic.lib.ahk");
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
@@ -1271,9 +1296,7 @@ fn main() -> Result<()> {
println!( println!(
"\nAHKv1 helper library for komorebic written to {}", "\nAHKv1 helper library for komorebic written to {}",
library.to_str().ok_or_else(|| anyhow!( library.to_string_lossy()
"could not find the path to the generated ahk lib file"
))?
); );
println!("\nYou can convert this file to AHKv2 syntax using https://github.com/mmikeww/AHK-v2-script-converter"); println!("\nYou can convert this file to AHKv2 syntax using https://github.com/mmikeww/AHK-v2-script-converter");
@@ -1285,8 +1308,7 @@ fn main() -> Result<()> {
println!("\n#Include komorebic.lib.ahk"); println!("\n#Include komorebic.lib.ahk");
} }
SubCommand::Log => { SubCommand::Log => {
let mut color_log = std::env::temp_dir(); let color_log = std::env::temp_dir().join("komorebi.log");
color_log.push("komorebi.log");
let file = TailedFile::new(File::open(color_log)?); let file = TailedFile::new(File::open(color_log)?);
let locked = file.lock(); let locked = file.lock();
#[allow(clippy::significant_drop_in_scrutinee)] #[allow(clippy::significant_drop_in_scrutinee)]
@@ -1486,7 +1508,7 @@ fn main() -> Result<()> {
&SocketMessage::WorkspaceLayoutCustom( &SocketMessage::WorkspaceLayoutCustom(
arg.monitor, arg.monitor,
arg.workspace, arg.workspace,
resolve_windows_path(&arg.path)?, resolve_home_path(arg.path)?,
) )
.as_bytes()?, .as_bytes()?,
)?; )?;
@@ -1495,7 +1517,7 @@ fn main() -> Result<()> {
send_message( send_message(
&SocketMessage::NamedWorkspaceLayoutCustom( &SocketMessage::NamedWorkspaceLayoutCustom(
arg.workspace, arg.workspace,
resolve_windows_path(&arg.path)?, resolve_home_path(arg.path)?,
) )
.as_bytes()?, .as_bytes()?,
)?; )?;
@@ -1527,7 +1549,7 @@ fn main() -> Result<()> {
arg.monitor, arg.monitor,
arg.workspace, arg.workspace,
arg.at_container_count, arg.at_container_count,
resolve_windows_path(&arg.path)?, resolve_home_path(arg.path)?,
) )
.as_bytes()?, .as_bytes()?,
)?; )?;
@@ -1537,7 +1559,7 @@ fn main() -> Result<()> {
&SocketMessage::NamedWorkspaceLayoutCustomRule( &SocketMessage::NamedWorkspaceLayoutCustomRule(
arg.workspace, arg.workspace,
arg.at_container_count, arg.at_container_count,
resolve_windows_path(&arg.path)?, resolve_home_path(arg.path)?,
) )
.as_bytes()?, .as_bytes()?,
)?; )?;
@@ -1573,11 +1595,11 @@ fn main() -> Result<()> {
} }
if arg.whkd && which("whkd").is_err() { if arg.whkd && which("whkd").is_err() {
return Err(anyhow!("could not find whkd, please make sure it is installed before using the --whkd flag")); bail!("could not find whkd, please make sure it is installed before using the --whkd flag");
} }
if arg.ahk && which(&ahk).is_err() { if arg.ahk && which(&ahk).is_err() {
return Err(anyhow!("could not find autohotkey, please make sure it is installed before using the --ahk flag")); bail!("could not find autohotkey, please make sure it is installed before using the --ahk flag");
} }
let mut buf: PathBuf; let mut buf: PathBuf;
@@ -1594,7 +1616,7 @@ fn main() -> Result<()> {
buf.pop(); // %USERPROFILE%\scoop\shims buf.pop(); // %USERPROFILE%\scoop\shims
buf.pop(); // %USERPROFILE%\scoop buf.pop(); // %USERPROFILE%\scoop
buf.push("apps\\komorebi\\current\\komorebi.exe"); //%USERPROFILE%\scoop\komorebi\current\komorebi.exe buf.push("apps\\komorebi\\current\\komorebi.exe"); //%USERPROFILE%\scoop\komorebi\current\komorebi.exe
Option::from(buf.to_str().ok_or_else(|| { Some(buf.to_str().ok_or_else(|| {
anyhow!("cannot create a string from the scoop komorebi path") anyhow!("cannot create a string from the scoop komorebi path")
})?) })?)
} }
@@ -1605,17 +1627,13 @@ fn main() -> Result<()> {
let mut flags = vec![]; let mut flags = vec![];
if let Some(config) = arg.config { if let Some(config) = arg.config {
let path = resolve_windows_path(config.as_os_str().to_str().unwrap())?; let path = resolve_home_path(config)?;
if !path.is_file() { if !path.is_file() {
return Err(anyhow!("could not find file: {}", path.to_string_lossy())); bail!("could not find file: {}", path.display());
} }
flags.push(format!( // we don't need to replace UNC prefix here as `resolve_home_path` already did
"'--config=\"{}\"'", flags.push(format!("'--config=\"{}\"'", path.display()));
path.as_os_str()
.to_string_lossy()
.trim_start_matches(r"\\?\"),
));
} }
if arg.ffm { if arg.ffm {
@@ -1670,12 +1688,12 @@ fn main() -> Result<()> {
} }
if arg.whkd { if arg.whkd {
let script = r#" let script = r"
if (!(Get-Process whkd -ErrorAction SilentlyContinue)) if (!(Get-Process whkd -ErrorAction SilentlyContinue))
{ {
Start-Process whkd -WindowStyle hidden Start-Process whkd -WindowStyle hidden
} }
"#; ";
match powershell_script::run(script) { match powershell_script::run(script) {
Ok(_) => { Ok(_) => {
println!("{script}"); println!("{script}");
@@ -1687,15 +1705,14 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
} }
if arg.ahk { if arg.ahk {
let home = HOME_DIR.clone(); let config_ahk = HOME_DIR.join("komorebi.ahk");
let mut config_ahk = home; let config_ahk = dunce::simplified(&config_ahk);
config_ahk.push("komorebi.ahk");
let script = format!( let script = format!(
r#" r#"
Start-Process {ahk} {config} -WindowStyle hidden Start-Process {ahk} {config} -WindowStyle hidden
"#, "#,
config = config_ahk.as_os_str().to_string_lossy() config = config_ahk.display()
); );
match powershell_script::run(&script) { match powershell_script::run(&script) {
@@ -1710,9 +1727,9 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
} }
SubCommand::Stop(arg) => { SubCommand::Stop(arg) => {
if arg.whkd { if arg.whkd {
let script = r#" let script = r"
Stop-Process -Name:whkd -ErrorAction SilentlyContinue Stop-Process -Name:whkd -ErrorAction SilentlyContinue
"#; ";
match powershell_script::run(script) { match powershell_script::run(script) {
Ok(_) => { Ok(_) => {
println!("{script}"); println!("{script}");
@@ -1777,7 +1794,7 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
} }
SubCommand::LoadCustomLayout(arg) => { SubCommand::LoadCustomLayout(arg) => {
send_message( send_message(
&SocketMessage::ChangeLayoutCustom(resolve_windows_path(&arg.path)?).as_bytes()?, &SocketMessage::ChangeLayoutCustom(resolve_home_path(arg.path)?).as_bytes()?,
)?; )?;
} }
SubCommand::FlipLayout(arg) => { SubCommand::FlipLayout(arg) => {
@@ -1843,74 +1860,12 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
)?; )?;
} }
SubCommand::State => { SubCommand::State => {
let home = DATA_DIR.clone(); with_komorebic_socket(|| send_message(&SocketMessage::State.as_bytes()?))?;
let mut socket = home;
socket.push("komorebic.sock");
let socket = socket.as_path();
match std::fs::remove_file(socket) {
Ok(_) => {}
Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {}
_ => {
return Err(error.into());
}
},
};
let listener = UnixListener::bind(socket)?;
send_message(&SocketMessage::State.as_bytes()?)?;
match listener.accept() {
Ok(incoming) => {
let stream = BufReader::new(incoming.0);
for line in stream.lines() {
println!("{}", line?);
}
return Ok(());
}
Err(error) => {
panic!("{}", error);
}
}
} }
SubCommand::Query(arg) => { SubCommand::Query(arg) => {
let home = DATA_DIR.clone(); with_komorebic_socket(|| {
let mut socket = home; send_message(&SocketMessage::Query(arg.state_query).as_bytes()?)
socket.push("komorebic.sock"); })?;
let socket = socket.as_path();
match std::fs::remove_file(socket) {
Ok(_) => {}
Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {}
_ => {
return Err(error.into());
}
},
};
let listener = UnixListener::bind(socket)?;
send_message(&SocketMessage::Query(arg.state_query).as_bytes()?)?;
match listener.accept() {
Ok(incoming) => {
let stream = BufReader::new(incoming.0);
for line in stream.lines() {
println!("{}", line?);
}
return Ok(());
}
Err(error) => {
panic!("{}", error);
}
}
} }
SubCommand::RestoreWindows => { SubCommand::RestoreWindows => {
let hwnd_json = DATA_DIR.join("komorebi.hwnd.json"); let hwnd_json = DATA_DIR.join("komorebi.hwnd.json");
@@ -1974,9 +1929,7 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
match target.identifier { match target.identifier {
ApplicationIdentifier::Exe => {} ApplicationIdentifier::Exe => {}
_ => { _ => {
return Err(anyhow!( bail!("this command requires applications to be identified by their exe");
"this command requires applications to be identified by their exe"
));
} }
} }
@@ -1998,10 +1951,10 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
send_message(&SocketMessage::QuickLoad.as_bytes()?)?; send_message(&SocketMessage::QuickLoad.as_bytes()?)?;
} }
SubCommand::SaveResize(arg) => { SubCommand::SaveResize(arg) => {
send_message(&SocketMessage::Save(resolve_windows_path(&arg.path)?).as_bytes()?)?; send_message(&SocketMessage::Save(resolve_home_path(arg.path)?).as_bytes()?)?;
} }
SubCommand::LoadResize(arg) => { SubCommand::LoadResize(arg) => {
send_message(&SocketMessage::Load(resolve_windows_path(&arg.path)?).as_bytes()?)?; send_message(&SocketMessage::Load(resolve_home_path(arg.path)?).as_bytes()?)?;
} }
SubCommand::Subscribe(arg) => { SubCommand::Subscribe(arg) => {
send_message(&SocketMessage::AddSubscriber(arg.named_pipe).as_bytes()?)?; send_message(&SocketMessage::AddSubscriber(arg.named_pipe).as_bytes()?)?;
@@ -2054,10 +2007,9 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
)?; )?;
} }
SubCommand::AhkAppSpecificConfiguration(arg) => { SubCommand::AhkAppSpecificConfiguration(arg) => {
let content = std::fs::read_to_string(resolve_windows_path(&arg.path)?)?; let content = std::fs::read_to_string(resolve_home_path(arg.path)?)?;
let lines = if let Some(override_path) = arg.override_path { let lines = if let Some(override_path) = arg.override_path {
let override_content = let override_content = std::fs::read_to_string(resolve_home_path(override_path)?)?;
std::fs::read_to_string(resolve_windows_path(&override_path)?)?;
ApplicationConfigurationGenerator::generate_ahk( ApplicationConfigurationGenerator::generate_ahk(
&content, &content,
@@ -2067,28 +2019,24 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
ApplicationConfigurationGenerator::generate_ahk(&content, None)? ApplicationConfigurationGenerator::generate_ahk(&content, None)?
}; };
let mut generated_config = HOME_DIR.clone(); let generated_config = HOME_DIR.join("komorebi.generated.ahk");
generated_config.push("komorebi.generated.ahk");
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
.truncate(true) .truncate(true)
.open(generated_config.clone())?; .open(&generated_config)?;
file.write_all(lines.join("\n").as_bytes())?; file.write_all(lines.join("\n").as_bytes())?;
println!( println!(
"\nApplication-specific generated configuration written to {}", "\nApplication-specific generated configuration written to {}",
generated_config.to_str().ok_or_else(|| anyhow!( generated_config.display()
"could not find the path to the generated configuration file"
))?
); );
} }
SubCommand::PwshAppSpecificConfiguration(arg) => { SubCommand::PwshAppSpecificConfiguration(arg) => {
let content = std::fs::read_to_string(resolve_windows_path(&arg.path)?)?; let content = std::fs::read_to_string(resolve_home_path(arg.path)?)?;
let lines = if let Some(override_path) = arg.override_path { let lines = if let Some(override_path) = arg.override_path {
let override_content = let override_content = std::fs::read_to_string(resolve_home_path(override_path)?)?;
std::fs::read_to_string(resolve_windows_path(&override_path)?)?;
ApplicationConfigurationGenerator::generate_pwsh( ApplicationConfigurationGenerator::generate_pwsh(
&content, &content,
@@ -2098,25 +2046,22 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
ApplicationConfigurationGenerator::generate_pwsh(&content, None)? ApplicationConfigurationGenerator::generate_pwsh(&content, None)?
}; };
let mut generated_config = HOME_DIR.clone(); let generated_config = HOME_DIR.join("komorebi.generated.ps1");
generated_config.push("komorebi.generated.ps1");
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
.truncate(true) .truncate(true)
.open(generated_config.clone())?; .open(&generated_config)?;
file.write_all(lines.join("\n").as_bytes())?; file.write_all(lines.join("\n").as_bytes())?;
println!( println!(
"\nApplication-specific generated configuration written to {}", "\nApplication-specific generated configuration written to {}",
generated_config.to_str().ok_or_else(|| anyhow!( generated_config.display()
"could not find the path to the generated configuration file"
))?
); );
} }
SubCommand::FormatAppSpecificConfiguration(arg) => { SubCommand::FormatAppSpecificConfiguration(arg) => {
let file_path = resolve_windows_path(&arg.path)?; let file_path = resolve_home_path(arg.path)?;
let content = std::fs::read_to_string(&file_path)?; let content = std::fs::read_to_string(&file_path)?;
let formatted_content = ApplicationConfigurationGenerator::format(&content)?; let formatted_content = ApplicationConfigurationGenerator::format(&content)?;
@@ -2134,8 +2079,7 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
let content = reqwest::blocking::get("https://raw.githubusercontent.com/LGUG2Z/komorebi-application-specific-configuration/master/applications.yaml")? let content = reqwest::blocking::get("https://raw.githubusercontent.com/LGUG2Z/komorebi-application-specific-configuration/master/applications.yaml")?
.text()?; .text()?;
let mut output_file = HOME_DIR.clone(); let output_file = HOME_DIR.join("applications.yaml");
output_file.push("applications.yaml");
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.write(true) .write(true)
@@ -2145,189 +2089,31 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
file.write_all(content.as_bytes())?; file.write_all(content.as_bytes())?;
let output_path = output_file.to_str().unwrap().to_string();
let output_path = output_path.replace('\\', "/");
println!("Latest version of applications.yaml from https://github.com/LGUG2Z/komorebi-application-specific-configuration downloaded\n"); println!("Latest version of applications.yaml from https://github.com/LGUG2Z/komorebi-application-specific-configuration downloaded\n");
println!( println!(
"You can add this to your komorebi.json static configuration file like this: \n\n\"app_specific_configuration_path\": \"{output_path}\"", "You can add this to your komorebi.json static configuration file like this: \n\n\"app_specific_configuration_path\": \"{}\"",
output_file.display()
); );
} }
SubCommand::NotificationSchema => { SubCommand::NotificationSchema => {
let home = DATA_DIR.clone(); with_komorebic_socket(|| send_message(&SocketMessage::NotificationSchema.as_bytes()?))?;
let mut socket = home;
socket.push("komorebic.sock");
let socket = socket.as_path();
match std::fs::remove_file(socket) {
Ok(_) => {}
Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {}
_ => {
return Err(error.into());
}
},
};
send_message(&SocketMessage::NotificationSchema.as_bytes()?)?;
let listener = UnixListener::bind(socket)?;
match listener.accept() {
Ok(incoming) => {
let stream = BufReader::new(incoming.0);
for line in stream.lines() {
println!("{}", line?);
}
return Ok(());
}
Err(error) => {
panic!("{}", error);
}
}
} }
SubCommand::SocketSchema => { SubCommand::SocketSchema => {
let home = DATA_DIR.clone(); with_komorebic_socket(|| send_message(&SocketMessage::SocketSchema.as_bytes()?))?;
let mut socket = home;
socket.push("komorebic.sock");
let socket = socket.as_path();
match std::fs::remove_file(socket) {
Ok(_) => {}
Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {}
_ => {
return Err(error.into());
}
},
};
send_message(&SocketMessage::SocketSchema.as_bytes()?)?;
let listener = UnixListener::bind(socket)?;
match listener.accept() {
Ok(incoming) => {
let stream = BufReader::new(incoming.0);
for line in stream.lines() {
println!("{}", line?);
}
return Ok(());
}
Err(error) => {
panic!("{}", error);
}
}
} }
SubCommand::StaticConfigSchema => { SubCommand::StaticConfigSchema => {
let home = DATA_DIR.clone(); with_komorebic_socket(|| send_message(&SocketMessage::StaticConfigSchema.as_bytes()?))?;
let mut socket = home;
socket.push("komorebic.sock");
let socket = socket.as_path();
match std::fs::remove_file(socket) {
Ok(_) => {}
Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {}
_ => {
return Err(error.into());
}
},
};
send_message(&SocketMessage::StaticConfigSchema.as_bytes()?)?;
let listener = UnixListener::bind(socket)?;
match listener.accept() {
Ok(incoming) => {
let stream = BufReader::new(incoming.0);
for line in stream.lines() {
println!("{}", line?);
}
return Ok(());
}
Err(error) => {
panic!("{}", error);
}
}
} }
SubCommand::GenerateStaticConfig => { SubCommand::GenerateStaticConfig => {
let home = DATA_DIR.clone(); with_komorebic_socket(|| {
let mut socket = home; send_message(&SocketMessage::GenerateStaticConfig.as_bytes()?)
socket.push("komorebic.sock"); })?;
let socket = socket.as_path();
match std::fs::remove_file(socket) {
Ok(_) => {}
Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {}
_ => {
return Err(error.into());
}
},
};
send_message(&SocketMessage::GenerateStaticConfig.as_bytes()?)?;
let listener = UnixListener::bind(socket)?;
match listener.accept() {
Ok(incoming) => {
let stream = BufReader::new(incoming.0);
for line in stream.lines() {
println!("{}", line?);
}
return Ok(());
}
Err(error) => {
panic!("{}", error);
}
}
} }
} }
Ok(()) Ok(())
} }
fn resolve_windows_path(raw_path: &str) -> Result<PathBuf> {
let path = if raw_path.starts_with('~') {
raw_path.replacen(
'~',
&dirs::home_dir()
.ok_or_else(|| anyhow!("there is no home directory"))?
.display()
.to_string(),
1,
)
} else {
raw_path.to_string()
};
let full_path = PathBuf::from(path);
let parent = full_path
.parent()
.ok_or_else(|| anyhow!("cannot parse directory"))?;
Ok(if parent.is_dir() {
let file = full_path
.components()
.last()
.ok_or_else(|| anyhow!("cannot parse filename"))?;
let mut canonicalized = std::fs::canonicalize(parent)?;
canonicalized.push(file);
canonicalized
} else {
full_path
})
}
fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) { fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
// BOOL is returned but does not signify whether or not the operation was succesful // BOOL is returned but does not signify whether or not the operation was succesful
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow