use std::fmt;
use std::io::Write;
use std::process::Child;
use std::process::Stdio;
use std::sync::RwLock;
pub struct Ueberzug(RwLock<Option<Child>>);
impl Ueberzug {
pub fn new() -> Self {
Self(RwLock::new(None))
}
pub fn draw(&self, config: &UeConf) {
let cmd = config.to_json();
if let Err(e) = self.run(&cmd) {
println!(
"could not draw {},from path {}",
config.identifier, config.path
);
println!("{}", e);
};
}
pub fn clear(&self, identifier: &str) {
let config = UeConf {
action: Actions::Remove,
identifier: identifier,
..Default::default()
};
let cmd = config.to_json();
self.run(&cmd).expect("Failed to Clear Image");
}
fn run(&self, cmd: &str) -> Result<(), std::io::Error> {
let mut ueberzug = self.0.write().unwrap();
if ueberzug.is_none() {
*ueberzug = Some(
std::process::Command::new("ueberzug")
.args(&["layer", "--silent"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?,
);
}
let stdin = (*ueberzug).as_mut().unwrap().stdin.as_mut().unwrap();
stdin.write_all(cmd.as_bytes())?;
Ok(())
}
}
pub enum Actions {
Add,
Remove,
}
impl fmt::Display for Actions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Actions::Add => write!(f, "add"),
&Actions::Remove => write!(f, "remove"),
}
}
}
#[derive(Clone, Copy)]
pub enum Scalers {
Crop,
Distort,
FitContain,
Contain,
ForcedCover,
Cover,
}
impl fmt::Display for Scalers {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Scalers::Contain => write!(f, "contain"),
&Scalers::Cover => write!(f, "cover"),
&Scalers::Crop => write!(f, "crop"),
&Scalers::Distort => write!(f, "distort"),
&Scalers::FitContain => write!(f, "fit_contain"),
&Scalers::ForcedCover => write!(f, "forced_cover"),
}
}
}
pub struct UeConf<'a> {
pub action: Actions,
pub identifier: &'a str,
pub x: u16,
pub y: u16,
pub path: &'a str,
pub width: Option<u16>,
pub height: Option<u16>,
pub scaler: Option<Scalers>,
pub draw: Option<bool>,
pub synchronously_draw: Option<bool>,
pub scaling_position_x: Option<f32>,
pub scaling_position_y: Option<f32>,
}
impl<'a> Default for UeConf<'a> {
fn default() -> Self {
Self {
action: Actions::Add,
identifier: "",
x: 0,
y: 0,
path: "",
width: None,
height: None,
scaler: None,
draw: None,
synchronously_draw: None,
scaling_position_x: None,
scaling_position_y: None,
}
}
}
macro_rules! if_not_none {
($st:expr,$name:expr,$val:expr) => {
match $val {
Some(z) => $st + &format!(",\"{}\":\"{}\"", $name, z),
None => $st,
}
};
}
impl<'a> UeConf<'a> {
fn to_json(&self) -> String {
if self.identifier == "" {
panic!("Incomplete Information : Itentifier Not Found");
}
match self.action {
Actions::Add => {
if self.path == "" {
panic!("Incomplete Information : Path empty");
}
let mut jsn = String::from(r#"{"action":"add","#);
jsn = jsn
+ &format!(
"\"path\":\"{}\",\"identifier\":\"{}\",\"x\":{},\"y\":{}",
self.path, self.identifier, self.x, self.y
);
jsn = if_not_none!(jsn, "width", self.width);
jsn = if_not_none!(jsn, "height", self.height);
jsn = if_not_none!(jsn, "scaler", self.scaler);
jsn = if_not_none!(jsn, "draw", self.draw);
jsn = if_not_none!(jsn, "sync", self.synchronously_draw);
jsn = if_not_none!(jsn, "scaling_position_x", self.scaling_position_x);
jsn = if_not_none!(jsn, "scaling_position_y", self.scaling_position_y);
jsn = jsn + "}\n";
jsn
}
Actions::Remove => format!(
"{{\"action\":\"remove\",\"identifier\":\"{}\"}}\n",
self.identifier
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn enum_to_str() {
let add = Actions::Add;
let remove = Actions::Remove;
assert_eq!(add.to_string(), "add");
assert_eq!(format!("{}", remove), "remove");
let scaler_1 = Scalers::Contain;
let scaler_2 = Scalers::FitContain;
assert_eq!(scaler_1.to_string(), "contain");
assert_eq!(scaler_2.to_string(), "fit_contain");
}
#[test]
fn json_convertion() {
let conf = UeConf {
identifier: "a",
path: "a",
..Default::default()
};
let rem_conf = UeConf {
action: Actions::Remove,
identifier: "a",
..Default::default()
};
assert_eq!(
conf.to_json(),
"{\"action\":\"add\",\"path\":\"a\",\"identifier\":\"a\",\"x\":0,\"y\":0}\n"
);
assert_eq!(
rem_conf.to_json(),
"{\"action\":\"remove\",\"identifier\":\"a\"}\n"
);
}
}