sway-de-utils/src/lib/profile.rs
2026-01-27 10:57:31 -08:00

342 lines
11 KiB
Rust

use {
crate::{
config::{Config, Profile},
get_xdg_dirs,
lib::{
SDUError,
cli::{DisplayMode, ProfileCommand, ProfileGetCommand, ProfileSwitchCommand},
get, get_sway_connection, run_sway_command, shortcuts_fn,
sway::focused_workspace_profile,
},
setup_runtime_dir,
},
log::{debug, error},
serde_json::json,
std::fs::{self, DirBuilder, write},
swayipc::Connection,
xdg::BaseDirectories,
};
pub fn profile_fn(
profile_command: &ProfileCommand,
profiles_config: Vec<Profile>,
preserve_keyboard_order: bool,
) -> Result<(), SDUError> {
let xdg_dirs = get_xdg_dirs();
setup_runtime_dir(xdg_dirs);
match profile_command {
ProfileCommand::Init => {
let active_profile_index = active_profile_index();
match switch_by_index(
profiles_config,
active_profile_index,
preserve_keyboard_order,
) {
Ok(_) => {
let _: () = debug!("successfully initialized sway-de-utils");
Ok(())
}
Err(e) => Err(e),
}
}
ProfileCommand::Switch { switch_command } => match switch_command {
Some(ProfileSwitchCommand::To { index, name, query }) => match index {
Some(i) => switch_by_index(profiles_config, *i, preserve_keyboard_order),
None => match name {
Some(n) => {
switch_by_name(profiles_config, n.to_string(), preserve_keyboard_order)
}
None => match query {
Some(q) => match q.parse::<usize>() {
Ok(i) => match switch_by_index(
profiles_config.clone(),
i,
preserve_keyboard_order,
) {
Ok(o) => Ok(o),
Err(_) => switch_by_name(
profiles_config,
q.to_string(),
preserve_keyboard_order,
),
},
Err(_) => switch_by_name(
profiles_config,
q.to_string(),
preserve_keyboard_order,
),
},
None => Err(SDUError {
message: "No profile index or name provided.".to_string(),
}),
},
},
},
Some(ProfileSwitchCommand::Next) => {
match next(profiles_config, preserve_keyboard_order) {
Ok(_) => {
debug!("Successfully switched to next profile");
Ok(())
}
Err(e) => Err(e),
}
}
Some(ProfileSwitchCommand::Prev) => {
match previous(profiles_config, preserve_keyboard_order) {
Ok(_) => {
debug!("successfully switched to previous profile");
Ok(())
}
Err(e) => Err(e),
}
}
None => {
for profile in profiles_config {
println!("{} {}", profile.icon, profile.name);
}
Ok(())
}
},
ProfileCommand::Get {
get_command,
monitor,
} => {
let profile_detail = get_command.clone().unwrap_or(ProfileGetCommand::Json);
println!(
"{}",
get::profile_info(profiles_config.clone(), profile_detail.clone())
);
let _: () = if monitor.unwrap_or_default() {
let _ = get::watch(
profiles_config,
profile_detail,
get_xdg_dirs()
.runtime_dir
.expect("XDG Runtime dir could not be found")
.join("sway-de-utils/active-profile.json")
.to_str()
.expect("could not create str from path")
.to_string(),
);
};
Ok(())
}
ProfileCommand::Shortcuts { shortcut_command } => {
let focused_profile = focused_workspace_profile(profiles_config);
shortcuts_fn(
shortcut_command,
focused_profile
.scripts
.expect("could not find scripts for profile"),
)
}
ProfileCommand::List { mode } => {
list_profiles(mode.unwrap_or(DisplayMode::Json), profiles_config)
}
}
}
//TODO: Consolidate this function with print_shortcuts in shortcuts.rs
fn list_profiles(mode: DisplayMode, profiles: Vec<Profile>) -> Result<(), SDUError> {
let output = match mode {
DisplayMode::Dmenu => {
let mut dmenu_entries = Vec::<String>::new();
for profile in profiles {
dmenu_entries.push(format!("{} {}", profile.icon, profile.name));
}
dmenu_entries.join("\n")
}
DisplayMode::Json => json!(profiles).to_string(),
};
println!("{}", output);
Ok(())
}
fn initialize(
mut sway_connection: Connection,
profile: Profile,
uindex: usize,
preserve_keyboard_order: bool,
) -> Result<(), SDUError> {
for i in 0..10 {
let workspace = (uindex * 10)
+ match i.eq(&0) && !preserve_keyboard_order {
true => 10,
false => i,
};
debug!("key {}: workspace {}", i, workspace);
debug!(
"bindsym $mod+{} workspace number {}:{}",
i, workspace, profile.icon
);
match run_sway_command(
&mut sway_connection,
format!(
"bindsym $mod+{} workspace number {}:{}",
i, workspace, profile.icon
),
) {
Ok(_) => (),
Err(_) => todo!(),
}
match run_sway_command(
&mut sway_connection,
format!(
"bindsym $mod+Shift+{} move container to workspace number {}:{}",
i, workspace, profile.icon
),
) {
Ok(_) => (),
Err(_) => todo!(),
}
}
let base_directories = BaseDirectories::new();
let active_profile_cache = base_directories
.get_runtime_directory()
.expect("why tf do xdg dirs not exist")
.join("sway-de-utils/active-profile.json");
let active_profile = json!(uindex);
DirBuilder::new()
.recursive(true)
.create(
active_profile_cache
.parent()
.expect("Unable to determine cache dir"),
)
.expect("Failed to create cache directory");
match write(active_profile_cache, active_profile.to_string()) {
Ok(o) => debug!("{:#?}", o),
Err(e) => debug!("{:#?}", e),
};
match run_sway_command(
&mut sway_connection,
format!("workspace number {}:{}", (uindex * 10) + 1, profile.icon),
) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
pub fn profile_from_index(profiles: Vec<Profile>, index: usize) -> Profile {
profiles
.get(index)
.expect("Profile not found for index")
.clone()
}
pub fn _profile_from_name(config: Config, name: String) -> Result<Profile, SDUError> {
match config.profiles.iter().find(|x| x.name == name) {
Some(p) => Ok(p.clone()),
None => Err(SDUError {
message: format!("Profile not found with name {}", name),
}),
}
}
fn switch_by_index(
profiles_config: Vec<Profile>,
index: usize,
preserve_keyboard_order: bool,
) -> Result<(), SDUError> {
let profile = profile_from_index(profiles_config, index);
match initialize(
get_sway_connection(),
profile,
index,
preserve_keyboard_order,
) {
Ok(_) => {
println!("successfully switched to profile at index {}", index);
Ok(())
}
Err(e) => Err(e),
}
}
fn switch_by_name(
profiles_config: Vec<Profile>,
name: String,
preserve_keyboard_order: bool,
) -> Result<(), SDUError> {
match index_from_name(profiles_config.clone(), name.clone()) {
Ok(index) => match switch_by_index(profiles_config, index, preserve_keyboard_order) {
Ok(_) => {
println!("Successfully switched to profile with name {}", name);
Ok(())
}
Err(e) => Err(e),
},
Err(e) => Err(e),
}
}
pub fn active_profile_index() -> usize {
let base_directories = BaseDirectories::new();
let active_profile_cache_json = base_directories
.get_runtime_directory()
.expect("xdg dirs do not exist?")
.join("sway-de-utils/active-profile.json");
if active_profile_cache_json.exists() {
fs::File::open(active_profile_cache_json)
.ok()
.and_then(|f| serde_json::from_reader::<fs::File, usize>(f).ok())
.expect("could not parse active profile cache file")
} else {
error!("no active profile cache file");
0
}
}
pub fn _active_profile(profiles: Vec<Profile>) -> Result<Profile, SDUError> {
match profiles.get(active_profile_index()) {
Some(p) => Ok(p.clone()),
None => Err(SDUError {
message: "Could not get profile by index".to_string(),
}),
}
}
fn next(profiles_config: Vec<Profile>, preserve_keyboard_order: bool) -> Result<(), SDUError> {
let profile_count = profiles_config.len();
let mut next_profile = active_profile_index() + 1;
if next_profile.ge(&profile_count) {
next_profile = 0
}
match switch_by_index(profiles_config, next_profile, preserve_keyboard_order) {
Ok(_) => {
println!("switched to next profile ({})", next_profile);
Ok(())
}
Err(e) => Err(e),
}
}
fn previous(profiles_config: Vec<Profile>, preserve_keyboard_order: bool) -> Result<(), SDUError> {
let profile_index = active_profile_index();
let prev_profile: usize = if profile_index.eq(&0) {
profiles_config.len() - 1
} else {
profile_index - 1
};
match switch_by_index(profiles_config, prev_profile, preserve_keyboard_order) {
Ok(_) => {
println!("switched to prev profile ({})", prev_profile);
Ok(())
}
Err(e) => Err(e),
}
}
pub fn index_from_name(profiles_config: Vec<Profile>, name: String) -> Result<usize, SDUError> {
match profiles_config.iter().position(|x| x.name == name) {
Some(i) => Ok(i),
None => Err(SDUError {
message: "Index not found for profile?".to_string(),
}),
}
}