diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c0b26b6..d066b83 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -22,7 +22,7 @@ jobs: targets: x86_64-unknown-linux-gnu - name: install dependencies run: | - sudo apt-get update; sudo apt-get install libcogl-pango-dev + sudo apt-get update; sudo apt-get install -y libcogl-pango-dev - name: Build run: | cargo build --release --target x86_64-unknown-linux-gnu diff --git a/src/item.rs b/src/item.rs new file mode 100644 index 0000000..84e5d26 --- /dev/null +++ b/src/item.rs @@ -0,0 +1,51 @@ +#[derive(serde::Deserialize, serde::Serialize)] +pub struct ItemContainer { + pub name: String, + pub item: Item, +} + +#[derive(serde::Deserialize, serde::Serialize)] +pub enum Item { + Bool { + #[serde(default)] + value: bool, + }, + Int { + #[serde(default)] + value: i64, + min: Option, + max: Option, + }, + Float { + #[serde(default)] + value: f64, + min: Option, + max: Option, + }, + String { + #[serde(default)] + value: String, + }, + Enum { + #[serde(default)] + value: usize, + options: Vec, + }, +} + +impl Item { + pub fn get_value(&self) -> String { + match self { + Item::Bool { value } => value.to_string(), + Item::Int { value, .. } => value.to_string(), + Item::Float { value, .. } => value.to_string(), + Item::String { value } => value.clone(), + Item::Enum { value, options } => options[*value].clone(), + } + } +} + +pub enum State { + Main, + Editing(usize), +} diff --git a/src/lib.rs b/src/lib.rs index 9fd8dc2..e09ba8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,56 +1,9 @@ +pub mod item; +pub mod mode_logic; + use std::io::Read; -#[derive(serde::Deserialize, serde::Serialize)] -struct ItemContainer { - name: String, - item: Item, -} - -#[derive(serde::Deserialize, serde::Serialize)] -enum Item { - Bool { - #[serde(default)] - value: bool, - }, - Int { - #[serde(default)] - value: i64, - min: Option, - max: Option, - }, - Float { - #[serde(default)] - value: f64, - min: Option, - max: Option, - }, - String { - #[serde(default)] - value: String, - }, - Enum { - #[serde(default)] - value: usize, - options: Vec, - }, -} - -impl Item { - fn get_value(&self) -> String { - match self { - Item::Bool { value } => value.to_string(), - Item::Int { value, .. } => value.to_string(), - Item::Float { value, .. } => value.to_string(), - Item::String { value } => value.clone(), - Item::Enum { value, options } => options[*value].clone(), - } - } -} - -enum State { - Main, - Editing(usize), -} +use crate::item::*; struct Mode<'rofi> { api: rofi_mode::Api<'rofi>, @@ -60,140 +13,6 @@ struct Mode<'rofi> { message: String, } -impl<'rofi> Mode<'rofi> { - fn entries_from_items(&mut self) { - self.state = State::Main; - self.api.set_display_name("edit"); - self.entries.clear(); - self.items.iter().for_each(|item| { - self.entries - .push(format!("{}: {}", item.name, item.item.get_value())); - }); - self.entries.push("Apply".to_string()); - } - - fn enter_edit(&mut self, item: usize) -> String { - self.state = State::Editing(item); - let item = &self.items[item]; - self.api.set_display_name(format!("editing {}", item.name)); - self.message = format!("Old value: {}", item.item.get_value()); - self.entries.clear(); - match &item.item { - Item::Bool { .. } => { - self.entries.push("true".to_string()); - self.entries.push("false".to_string()); - }, - Item::Int { value, min, max } => { - let min = min - .map(|min| format!("Min: {};", min)) - .unwrap_or("".to_string()); - let max = max - .map(|max| format!("Max: {};", max)) - .unwrap_or("".to_string()); - self.message = format!("Old value: {};\r{} {}", value, min, max); - }, - Item::Float { value, min, max } => { - let min = min - .map(|min| format!("Min: {};", min)) - .unwrap_or("".to_string()); - let max = max - .map(|max| format!("Max: {};", max)) - .unwrap_or("".to_string()); - self.message = format!("Old value: {};\r{} {}", value, min, max); - }, - Item::String { .. } => {}, - Item::Enum { options, .. } => { - options - .iter() - .for_each(|option| self.entries.push(option.clone())); - }, - } - self.entries.push("Cancel".to_string()); - return item.item.get_value(); - } - - fn finish_edit( - &mut self, - selected: Option, - custom_input: Option<&mut rofi_mode::String>, - ) { - match self.state { - State::Main => { - panic!("finish_edit called in main state") - }, - State::Editing(item) => { - let item = &mut self.items[item]; - match item.item { - Item::Bool { .. } => match selected { - Some(0) => item.item = Item::Bool { value: true }, - Some(1) => item.item = Item::Bool { value: false }, - _ => {}, - }, - Item::Int { value, min, max } => { - let mut new_value = value.clone(); - if let Some(input) = custom_input { - new_value = input.replace("\\-", "-").parse().unwrap(); - } - if let Some(min) = min { - if new_value < min { - new_value = min; - } - } - if let Some(max) = max { - if new_value > max { - new_value = max; - } - } - item.item = Item::Int { - value: new_value, - min, - max, - }; - }, - Item::Float { value, min, max } => { - let mut new_value = value.clone(); - if let Some(input) = custom_input { - new_value = input.replace("\\-", "-").parse().unwrap(); - } - if let Some(min) = min { - if new_value < min { - new_value = min; - } - } - if let Some(max) = max { - if new_value > max { - new_value = max; - } - } - item.item = Item::Float { - value: new_value, - min, - max, - }; - }, - Item::String { .. } => { - if let Some(input) = custom_input { - item.item = Item::String { - value: input.into(), - }; - } - }, - Item::Enum { ref mut value, .. } => { - if let Some(selected) = selected { - *value = selected; - } - }, - }; - self.entries_from_items(); - }, - } - } - - fn print_items(&self) { - println!("{}", serde_json::to_string(&self.items).unwrap()); - } -} - // reads until 2 subsequent newlines fn read_stdin() -> String { let mut inp = Vec::new(); @@ -214,7 +33,7 @@ impl<'rofi> rofi_mode::Mode<'rofi> for Mode<'rofi> { fn init(mut api: rofi_mode::Api<'rofi>) -> Result { api.set_display_name("edit"); - let args = std::env::args().into_iter().collect::>(); + let args = std::env::args().collect::>(); if args.contains(&"--edit-help".to_string()) { eprintln!( @@ -228,57 +47,52 @@ impl<'rofi> rofi_mode::Mode<'rofi> for Mode<'rofi> { return Err(()); } if args.contains(&"--edit-example".to_string()) { - let mut its: Vec = Vec::new(); - its.push(ItemContainer { - name: "bool item".to_string(), - item: Item::Bool { value: false }, - }); - its.push(ItemContainer { - name: "int item".to_string(), - item: Item::Int { - value: 0, - min: Some(0), - max: None, + let its: Vec = vec![ + ItemContainer { + name: "bool item".to_string(), + item: Item::Bool { value: false }, }, - }); - its.push(ItemContainer { - name: "float item".to_string(), - item: Item::Float { - value: 0.0, - min: None, - max: Some(69.0), + ItemContainer { + name: "int item".to_string(), + item: Item::Int { + value: 0, + min: Some(0), + max: None, + }, }, - }); - its.push(ItemContainer { - name: "string item".to_string(), - item: Item::String { - value: "hello".to_string(), + ItemContainer { + name: "float item".to_string(), + item: Item::Float { + value: 0.0, + min: None, + max: Some(69.0), + }, }, - }); - its.push(ItemContainer { - name: "enum items".to_string(), - item: Item::Enum { - value: 0, - options: vec!["opt1".into(), "opt2".into(), "other_opt".into()], + ItemContainer { + name: "string item".to_string(), + item: Item::String { + value: "hello".to_string(), + }, }, - }); + ItemContainer { + name: "enum items".to_string(), + item: Item::Enum { + value: 0, + options: vec!["opt1".into(), "opt2".into(), "other_opt".into()], + }, + }, + ]; eprintln!("Example input:\n{}\n\nOutput is the same with changed value keys\nKeys with value of null can be omited", serde_json::to_string_pretty(&its).unwrap()); return Err(()); } let items: Vec = if args.contains(&"--edit-stdin".to_string()) { let res = read_stdin(); serde_json::from_str(&res).unwrap() - } else if let Some(file_pos) = args - .iter() - .position(|arg| *arg == "--edit-file".to_string()) - { + } else if let Some(file_pos) = args.iter().position(|arg| arg == "--edit-file") { let fp = args[file_pos + 1].clone(); let res = std::fs::read_to_string(fp).unwrap(); serde_json::from_str(&res).unwrap() - } else if let Some(input_pos) = args - .iter() - .position(|arg| *arg == "--edit-input".to_string()) - { + } else if let Some(input_pos) = args.iter().position(|arg| arg == "--edit-input") { let res = args[input_pos + 1].clone(); serde_json::from_str(&res).unwrap() } else { @@ -318,13 +132,11 @@ impl<'rofi> rofi_mode::Mode<'rofi> for Mode<'rofi> { rofi_mode::Event::Ok { selected, .. } => { if let State::Editing(_item) = self.state { self.finish_edit(Some(selected), None); + } else if selected == self.entries() - 1 { + self.print_items(); + return rofi_mode::Action::Exit; } else { - if selected == self.entries() - 1 { - self.print_items(); - return rofi_mode::Action::Exit; - } else { - self.enter_edit(selected); - } + self.enter_edit(selected); } }, rofi_mode::Event::CustomInput { .. } => { @@ -335,11 +147,10 @@ impl<'rofi> rofi_mode::Mode<'rofi> for Mode<'rofi> { *input = rofi_mode::format!(""); } }, - rofi_mode::Event::Complete { selected } => match selected { - None => {}, - Some(item) => { - *input = rofi_mode::format!("{}", self.entries[item]); - }, + rofi_mode::Event::Complete { + selected: Some(item), + } => { + *input = rofi_mode::format!("{}", self.entries[item]); }, _ => {}, } diff --git a/src/mode_logic.rs b/src/mode_logic.rs new file mode 100644 index 0000000..3f51fb0 --- /dev/null +++ b/src/mode_logic.rs @@ -0,0 +1,135 @@ +use crate::{item::*, Mode}; + +impl<'rofi> Mode<'rofi> { + pub fn entries_from_items(&mut self) { + self.state = State::Main; + self.api.set_display_name("edit"); + self.entries.clear(); + self.items.iter().for_each(|item| { + self.entries + .push(format!("{}: {}", item.name, item.item.get_value())); + }); + self.entries.push("Apply".to_string()); + } + + pub fn enter_edit(&mut self, item: usize) -> String { + self.state = State::Editing(item); + let item = &self.items[item]; + self.api.set_display_name(format!("editing {}", item.name)); + self.message = format!("Old value: {}", item.item.get_value()); + self.entries.clear(); + match &item.item { + Item::Bool { .. } => { + self.entries.push("true".to_string()); + self.entries.push("false".to_string()); + }, + Item::Int { value, min, max } => { + let min = min + .map(|min| format!("Min: {};", min)) + .unwrap_or("".to_string()); + let max = max + .map(|max| format!("Max: {};", max)) + .unwrap_or("".to_string()); + self.message = format!("Old value: {};\r{} {}", value, min, max); + }, + Item::Float { value, min, max } => { + let min = min + .map(|min| format!("Min: {};", min)) + .unwrap_or("".to_string()); + let max = max + .map(|max| format!("Max: {};", max)) + .unwrap_or("".to_string()); + self.message = format!("Old value: {};\r{} {}", value, min, max); + }, + Item::String { .. } => {}, + Item::Enum { options, .. } => { + options + .iter() + .for_each(|option| self.entries.push(option.clone())); + }, + } + self.entries.push("Cancel".to_string()); + item.item.get_value() + } + + pub fn finish_edit( + &mut self, + selected: Option, + custom_input: Option<&mut rofi_mode::String>, + ) { + match self.state { + State::Main => { + panic!("finish_edit called in main state") + }, + State::Editing(item) => { + let item = &mut self.items[item]; + match item.item { + Item::Bool { .. } => match selected { + Some(0) => item.item = Item::Bool { value: true }, + Some(1) => item.item = Item::Bool { value: false }, + _ => {}, + }, + Item::Int { value, min, max } => { + let mut new_value = value; + if let Some(input) = custom_input { + new_value = input.replace("\\-", "-").parse().unwrap(); + } + if let Some(min) = min { + if new_value < min { + new_value = min; + } + } + if let Some(max) = max { + if new_value > max { + new_value = max; + } + } + item.item = Item::Int { + value: new_value, + min, + max, + }; + }, + Item::Float { value, min, max } => { + let mut new_value = value; + if let Some(input) = custom_input { + new_value = input.replace("\\-", "-").parse().unwrap(); + } + if let Some(min) = min { + if new_value < min { + new_value = min; + } + } + if let Some(max) = max { + if new_value > max { + new_value = max; + } + } + item.item = Item::Float { + value: new_value, + min, + max, + }; + }, + Item::String { .. } => { + if let Some(input) = custom_input { + item.item = Item::String { + value: input.into(), + }; + } + }, + Item::Enum { ref mut value, .. } => { + if let Some(selected) = selected { + *value = selected; + } + }, + }; + self.entries_from_items(); + }, + } + } + + pub fn print_items(&self) { + println!("{}", serde_json::to_string(&self.items).unwrap()); + } +}