pub use async_trait::async_trait;
use derive_more::Display;
use serde::{Deserialize, Deserializer, Serialize};
use std::env::{var, VarError};
use std::hash::{Hash, Hasher};
use std::{error, fmt, io};
#[derive(Debug)]
pub enum HyprError {
SerdeError(serde_json::Error),
IoError(io::Error),
FromUtf8Error(std::string::FromUtf8Error),
NotOkDispatch(String),
}
impl From<io::Error> for HyprError {
fn from(error: io::Error) -> Self {
HyprError::IoError(error)
}
}
impl From<serde_json::Error> for HyprError {
fn from(error: serde_json::Error) -> Self {
HyprError::SerdeError(error)
}
}
impl From<std::string::FromUtf8Error> for HyprError {
fn from(error: std::string::FromUtf8Error) -> Self {
HyprError::FromUtf8Error(error)
}
}
impl fmt::Display for HyprError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::IoError(err) => err.to_string(),
Self::SerdeError(err) => err.to_string(),
Self::FromUtf8Error(err) => err.to_string(),
Self::NotOkDispatch(msg) => format!(
"A dispatcher retrurned a non `ok`, value which is probably a error: {msg} was returned by it"
),
}
)
}
}
impl error::Error for HyprError {}
#[deprecated(since = "0.3.1", note = "New location: hyprland::Result")]
pub type HResult<T> = Result<T, HyprError>;
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Address(String);
#[async_trait]
pub trait HyprData {
fn get() -> crate::Result<Self>
where
Self: Sized;
async fn get_async() -> crate::Result<Self>
where
Self: Sized;
}
#[async_trait]
pub trait HyprDataActive {
fn get_active() -> crate::Result<Self>
where
Self: Sized;
async fn get_active_async() -> crate::Result<Self>
where
Self: Sized;
}
#[async_trait]
pub trait HyprDataActiveOptional {
fn get_active() -> crate::Result<Option<Self>>
where
Self: Sized;
async fn get_active_async() -> crate::Result<Option<Self>>
where
Self: Sized;
}
pub trait HyprDataVec<T>: HyprData {
fn to_vec(self) -> Vec<T>;
}
pub type WorkspaceId = i32;
fn ser_spec_opt(opt: &Option<String>) -> String {
match opt {
Some(name) => format!("special:{name}"),
None => "special".to_string(),
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Display, PartialOrd, Ord)]
#[serde(untagged)]
pub enum WorkspaceType {
#[display(fmt = "{}", "_0")]
Regular(
String,
),
#[display(fmt = "{}", "ser_spec_opt(_0)")]
Special(
Option<String>,
),
}
impl From<&WorkspaceType> for String {
fn from(value: &WorkspaceType) -> Self {
value.to_string()
}
}
impl From<i32> for WorkspaceType {
fn from(int: i32) -> Self {
match int {
1.. => WorkspaceType::Regular(int.to_string()),
_ => panic!("Unrecognised id"),
}
}
}
impl Hash for WorkspaceType {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
WorkspaceType::Regular(name) => name.hash(state),
WorkspaceType::Special(value) => match value {
Some(name) => name.hash(state),
None => "".hash(state),
},
}
}
}
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Address {
pub fn as_vec(self) -> Vec<u8> {
let Address(value) = self;
match hex::decode(value.trim_start_matches("0x")) {
Ok(value) => value,
Err(error) => panic!("A error has occured while parsing string as hex: {error}"),
}
}
pub fn new<T: ToString>(string: T) -> Self {
Self(string.to_string())
}
}
pub(crate) async fn write_to_socket(
path: String,
content: CommandContent,
) -> crate::Result<String> {
use crate::unix_async::*;
let mut stream = UnixStream::connect(path).await?;
stream.write_all(&content.as_bytes()).await?;
let mut response = vec![];
const BUF_SIZE: usize = 8192;
let mut buf = [0; BUF_SIZE];
loop {
let num_read = stream.read(&mut buf).await?;
let buf = &buf[..num_read];
response.append(&mut buf.to_vec());
if num_read == 0 || num_read != BUF_SIZE {
break;
}
}
Ok(String::from_utf8(response)?)
}
pub(crate) fn write_to_socket_sync(path: String, content: CommandContent) -> crate::Result<String> {
use io::prelude::*;
use std::os::unix::net::UnixStream;
let mut stream = UnixStream::connect(path)?;
stream.write_all(&content.as_bytes())?;
let mut response = vec![];
const BUF_SIZE: usize = 8192;
let mut buf = [0; BUF_SIZE];
loop {
let num_read = stream.read(&mut buf)?;
let buf = &buf[..num_read];
response.append(&mut buf.to_vec());
if num_read == 0 || num_read != BUF_SIZE {
break;
}
}
Ok(String::from_utf8(response)?)
}
pub(crate) enum SocketType {
Command,
Listener,
}
pub(crate) fn get_socket_path(socket_type: SocketType) -> String {
let hypr_instance_sig = match var("HYPRLAND_INSTANCE_SIGNATURE") {
Ok(var) => var,
Err(VarError::NotPresent) => panic!("Is hyprland running?"),
Err(VarError::NotUnicode(_)) => panic!("wtf no unicode?"),
};
let socket_name = match socket_type {
SocketType::Command => ".socket.sock",
SocketType::Listener => ".socket2.sock",
};
format!("/tmp/hypr/{hypr_instance_sig}/{socket_name}")
}
#[macro_export]
macro_rules! command {
($flag:ident, $($k:tt)*) => {{
CommandContent {
flag: CommandFlag::$flag,
data: format!($($k)*),
}
}};
}
pub use command;
pub enum CommandFlag {
JSON,
Empty,
}
pub struct CommandContent {
pub flag: CommandFlag,
pub data: String,
}
impl CommandContent {
pub fn as_bytes(&self) -> Vec<u8> {
self.to_string().into_bytes()
}
}
impl fmt::Display for CommandContent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.flag {
CommandFlag::JSON => write!(f, "j/{}", &self.data),
CommandFlag::Empty => write!(f, "/{}", &self.data),
}
}
}