hypr-helper 0.1.1

Hyprland Utilities such as adjusting gaps, getting windowname and workspace information
use crate::utils::{exec, get_pwd, get_reader};
use serde::{Deserialize, Serialize};
use std::io::BufRead;

/// Data structure to hold information of 1 workspace
#[derive(Debug, Serialize, Deserialize)]
pub struct Workspace {
    /// Workspace ID
    pub id: i32,
    /// Can be numbers 0-3
    /// 0: empty workspace
    /// 1: occupied workspace
    /// 2: active workspace
    pub status: u8,
    /// Which monitor the workspace is assigned to
    pub monitor: String,
}

impl Workspace {}

/// Data structure of workspace information as returned by hyprctl
#[derive(Debug, Deserialize)]
struct HyprWorkspace {
    id: i32,
    monitor: String,
    windows: i32,
}

/// Listen to events from the hyprland socket.
///
/// Print list of workspaces when workspace event is fired.
pub fn print_workspaces() {
    let input = get_reader();
    // just loop through each line we get.
    for line in input.lines() {
        match line {
            Ok(line) => {
                // Split the line into its verb and info
                let mut line_iterator = line.splitn(2, ">>");
                let verb = line_iterator.next().unwrap();
                // Ignore the info for now
                let _info = line_iterator.next().unwrap();
                let workspace_verbs = ["workspace", "focusedmon"];
                if workspace_verbs.contains(&verb) {
                    print_workspace();
                }
            }
            Err(_) => {}
        }
    }
}

/// Prints data from `get_workspace_hyprctl()`
fn print_workspace() {
    // First lets get the output from get_workspace_hyprctl()
    let workspaces = &get_workspace().unwrap();
    // Lets convert this to a json string.
    let workspaces_json = match serde_json::to_string(workspaces) {
        Ok(w) => w,
        Err(err) => format!("Could not parse workspaces: {}", err),
    };
    println!("{}", workspaces_json);
}

/// Retrieves workspace data using hyprctl
///
/// This is only used because when we listen from the signals we know the active workspace
/// but not the workspace that have windows opened.
fn get_workspace() -> Result<Vec<Workspace>, std::io::Error> {
    match exec("hyprctl workspaces -j", get_pwd(None).as_path()) {
        Ok(o) => {
            // Get valid output
            let output = match String::from_utf8(o.stdout) {
                Ok(output) => output,
                Err(err) => {
                    println!("Error parsing hyprctl output: {err}");
                    std::process::exit(1);
                }
            };

            // Now we try to get the active workspace
            let active_id: i32 = match exec("hyprctl activeworkspace -j", get_pwd(None).as_path()) {
                Ok(o) => match String::from_utf8(o.stdout) {
                    Ok(output) => {
                        match serde_json::from_str::<HyprWorkspace>(&output.to_string()) {
                            Ok(workspace) => workspace.id,
                            Err(err) => {
                                println!("Error parsing hyprctl output: {err}");
                                std::process::exit(1);
                            }
                        }
                    }
                    Err(err) => {
                        println!("Error getting hyprctl output: {err}");
                        std::process::exit(1);
                    }
                },

                Err(err) => {
                    println!("Error: {err}");
                    std::process::exit(1);
                }
            };

            let hypr_list: Vec<HyprWorkspace> = match serde_json::from_str(&output.to_string()) {
                Ok(vec) => vec,
                Err(err) => {
                    println!("Couldn't parse input from hyprctl: {err}");
                    std::process::exit(1);
                }
            };

            let mut list: Vec<Workspace> = hypr_list
                .iter()
                .map(|w| Workspace {
                    id: w.id,
                    status: get_status(w.id, w.windows, active_id),
                    monitor: w.monitor.clone(),
                })
                .collect();

            // Now sort the list by ID and return it
            list.sort_by(|a, b| a.id.cmp(&b.id));
            return Ok(list);
        }
        Err(err) => {
            println!("Error executing hyprctl: {err}");
            std::process::exit(1);
        }
    }
}

fn get_status(id: i32, windows: i32, active_id: i32) -> u8 {
    if id == active_id {
        return 2;
    } else if windows > 0 {
        return 1;
    } else {
        return 0;
    }
}