Add lot's of features
This commit is contained in:
parent
f173ec04e3
commit
f7021201ca
4 changed files with 294 additions and 108 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -197,6 +197,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"gdk",
|
"gdk",
|
||||||
"gio",
|
"gio",
|
||||||
|
"glib",
|
||||||
"gtk",
|
"gtk",
|
||||||
"hocon",
|
"hocon",
|
||||||
"regex",
|
"regex",
|
||||||
|
|
|
@ -10,6 +10,7 @@ edition = "2018"
|
||||||
gtk = { version = "0.9", features = [ "v3_16" ] }
|
gtk = { version = "0.9", features = [ "v3_16" ] }
|
||||||
gdk = { version = "", features = ["v3_16"] }
|
gdk = { version = "", features = ["v3_16"] }
|
||||||
gio = { version = "", features = ["v2_44"] }
|
gio = { version = "", features = ["v2_44"] }
|
||||||
|
glib = { version = "", features = ["v2_44"] }
|
||||||
|
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,125 @@
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
|
use glib::{types, value};
|
||||||
use hocon::*;
|
use hocon::*;
|
||||||
use hocon_ext::HoconExt;
|
use hocon_ext::HoconExt;
|
||||||
use std::{collections::HashMap, convert::TryFrom};
|
use std::collections::HashMap;
|
||||||
use try_match::try_match;
|
use try_match::try_match;
|
||||||
|
|
||||||
pub mod hocon_ext;
|
pub mod hocon_ext;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AttrValue(pub String);
|
pub struct EwwConfig {
|
||||||
|
widgets: HashMap<String, WidgetDefinition>,
|
||||||
|
windows: HashMap<String, EwwWindowDefinition>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
impl EwwConfig {
|
||||||
|
pub fn from_hocon(hocon: &Hocon) -> Result<EwwConfig> {
|
||||||
|
let data = hocon
|
||||||
|
.as_hash()
|
||||||
|
.context("eww config has to be a map structure")?;
|
||||||
|
|
||||||
|
Ok(EwwConfig {
|
||||||
|
widgets: data
|
||||||
|
.get("widgets")
|
||||||
|
.context("widgets need to be provided")?
|
||||||
|
.as_hash()
|
||||||
|
.context("widgets need to be a map")?
|
||||||
|
.iter()
|
||||||
|
.map(|(name, def)| Ok((name.clone(), parse_widget_definition(name.clone(), def)?)))
|
||||||
|
.collect::<Result<HashMap<String, WidgetDefinition>>>()?,
|
||||||
|
windows: data
|
||||||
|
.get("windows")
|
||||||
|
.context("windows need to be provided")?
|
||||||
|
.as_hash()
|
||||||
|
.context("windows need to be a map")?
|
||||||
|
.iter()
|
||||||
|
.map(|(name, def)| Ok((name.clone(), EwwWindowDefinition::from_hocon(def)?)))
|
||||||
|
.collect::<Result<HashMap<String, EwwWindowDefinition>>>()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widgets(&self) -> &HashMap<String, WidgetDefinition> {
|
||||||
|
&self.widgets
|
||||||
|
}
|
||||||
|
pub fn windows(&self) -> &HashMap<String, EwwWindowDefinition> {
|
||||||
|
&self.windows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct EwwWindowDefinition {
|
||||||
|
pub position: (i32, i32),
|
||||||
|
pub size: (i32, i32),
|
||||||
|
pub widget: ElementUse,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EwwWindowDefinition {
|
||||||
|
pub fn from_hocon(hocon: &Hocon) -> Result<EwwWindowDefinition> {
|
||||||
|
let data = hocon
|
||||||
|
.as_hash()
|
||||||
|
.context("window config has to be a map structure")?;
|
||||||
|
let position: Option<_> = try {
|
||||||
|
(
|
||||||
|
data.get("pos")?.as_hash()?.get("x")?.as_i64()? as i32,
|
||||||
|
data.get("pos")?.as_hash()?.get("y")?.as_i64()? as i32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let size: Option<_> = try {
|
||||||
|
(
|
||||||
|
data.get("size")?.as_hash()?.get("x")?.as_i64()? as i32,
|
||||||
|
data.get("size")?.as_hash()?.get("y")?.as_i64()? as i32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let element =
|
||||||
|
parse_element_use(data.get("widget").context("no widget use given")?.clone())?;
|
||||||
|
|
||||||
|
Ok(EwwWindowDefinition {
|
||||||
|
position: position.context("pos.x and pos.y need to be set")?,
|
||||||
|
size: size.context("size.x and size.y need to be set")?,
|
||||||
|
widget: element,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum AttrValue {
|
||||||
|
String(String),
|
||||||
|
Number(f64),
|
||||||
|
Boolean(bool),
|
||||||
|
VarRef(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttrValue {
|
||||||
|
pub fn as_string(&self) -> Option<&String> {
|
||||||
|
try_match!(AttrValue::String(x) = self).ok()
|
||||||
|
}
|
||||||
|
pub fn as_f64(&self) -> Option<f64> {
|
||||||
|
try_match!(AttrValue::Number(x) = self => *x).ok()
|
||||||
|
}
|
||||||
|
pub fn as_bool(&self) -> Option<bool> {
|
||||||
|
try_match!(AttrValue::Boolean(x) = self => *x).ok()
|
||||||
|
}
|
||||||
|
pub fn as_var_ref(&self) -> Option<&String> {
|
||||||
|
try_match!(AttrValue::VarRef(x) = self).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct WidgetDefinition {
|
pub struct WidgetDefinition {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub structure: ElementUse,
|
pub structure: ElementUse,
|
||||||
|
pub size: Option<(i32, i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ElementUse {
|
pub enum ElementUse {
|
||||||
Widget(WidgetUse),
|
Widget(WidgetUse),
|
||||||
Text(String),
|
Text(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct WidgetUse {
|
pub struct WidgetUse {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub children: Vec<ElementUse>,
|
pub children: Vec<ElementUse>,
|
||||||
|
@ -44,18 +142,25 @@ impl From<WidgetUse> for ElementUse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_widget_definition(text: &str) -> Result<WidgetDefinition> {
|
pub fn parse_widget_definition(name: String, hocon: &Hocon) -> Result<WidgetDefinition> {
|
||||||
let hocon = parse_hocon(text)?;
|
|
||||||
|
|
||||||
let definition = hocon
|
let definition = hocon
|
||||||
.as_hash()
|
.as_hash()
|
||||||
.ok_or_else(|| anyhow!("{:?} is not a hash", text))?;
|
.context("widget definition was not a hash")?;
|
||||||
|
let structure = definition
|
||||||
|
.get("structure")
|
||||||
|
.cloned()
|
||||||
|
.context("structure needs to be set")
|
||||||
|
.and_then(parse_element_use)?;
|
||||||
|
|
||||||
Ok(WidgetDefinition {
|
Ok(WidgetDefinition {
|
||||||
name: definition["name"]
|
name,
|
||||||
.as_string()
|
structure,
|
||||||
.context("name was not a string")?,
|
size: try {
|
||||||
structure: parse_element_use(definition.get("structure").unwrap().clone())?,
|
(
|
||||||
|
definition.get("size_x")?.as_i64()? as i32,
|
||||||
|
definition.get("size_y")?.as_i64()? as i32,
|
||||||
|
)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,12 +195,13 @@ pub fn parse_widget_use(data: HashMap<String, Hocon>) -> Result<WidgetUse> {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(key, value)| {
|
.filter_map(|(key, value)| {
|
||||||
Some((
|
Some((
|
||||||
key.clone(),
|
key.to_lowercase(),
|
||||||
match value {
|
match value {
|
||||||
Hocon::String(s) => AttrValue(s.to_string()),
|
Hocon::String(s) if s.starts_with("$$") => AttrValue::String(s.to_string()),
|
||||||
Hocon::Integer(n) => AttrValue(format!("{}", n)),
|
Hocon::String(s) => AttrValue::String(s.to_string()),
|
||||||
Hocon::Real(n) => AttrValue(format!("{}", n)),
|
Hocon::Integer(n) => AttrValue::Number(*n as f64),
|
||||||
Hocon::Boolean(b) => AttrValue(format!("{}", b)),
|
Hocon::Real(n) => AttrValue::Number(*n as f64),
|
||||||
|
Hocon::Boolean(b) => AttrValue::Boolean(*b),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
@ -137,31 +243,31 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_parse_widget_definition() {
|
// fn test_parse_widget_definition() {
|
||||||
let expected = WidgetDefinition {
|
// let expected = WidgetDefinition {
|
||||||
name: "example_widget".to_string(),
|
// name: "example_widget".to_string(),
|
||||||
structure: ElementUse::Widget(WidgetUse {
|
// structure: ElementUse::Widget(WidgetUse {
|
||||||
name: "layout_horizontal".to_string(),
|
// name: "layout_horizontal".to_string(),
|
||||||
attrs: HashMap::new(),
|
// attrs: HashMap::new(),
|
||||||
children: vec![
|
// children: vec![
|
||||||
ElementUse::Widget(WidgetUse::new(
|
// ElementUse::Widget(WidgetUse::new(
|
||||||
"text".to_string(),
|
// "text".to_string(),
|
||||||
vec![ElementUse::Text("hi".to_string())],
|
// vec![ElementUse::Text("hi".to_string())],
|
||||||
)),
|
// )),
|
||||||
ElementUse::Widget(WidgetUse::new("text".to_string(), vec![])),
|
// ElementUse::Widget(WidgetUse::new("text".to_string(), vec![])),
|
||||||
],
|
// ],
|
||||||
}),
|
// }),
|
||||||
};
|
// };
|
||||||
|
|
||||||
let parsed_hocon = parse_hocon("{ text: { children: \"hi\" } }").unwrap();
|
// let parsed_hocon = parse_hocon("{ text: { children: \"hi\" } }").unwrap();
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
parse_element_use(parsed_hocon).unwrap(),
|
// parse_element_use(parsed_hocon).unwrap(),
|
||||||
ElementUse::Widget(WidgetUse::new(
|
// ElementUse::Widget(WidgetUse::new(
|
||||||
"text".to_string(),
|
// "text".to_string(),
|
||||||
vec![ElementUse::Text("hi".to_string())]
|
// vec![ElementUse::Text("hi".to_string())]
|
||||||
))
|
// ))
|
||||||
);
|
// );
|
||||||
assert_eq!(parse_widget_definition(EXAMPLE_CONFIG).unwrap(), expected);
|
// assert_eq!(parse_widget_definition(EXAMPLE_CONFIG).unwrap(), expected);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
196
src/main.rs
196
src/main.rs
|
@ -2,51 +2,95 @@
|
||||||
extern crate gio;
|
extern crate gio;
|
||||||
extern crate gtk;
|
extern crate gtk;
|
||||||
|
|
||||||
use anyhow::{self, Context, Result};
|
use anyhow::{self, Result};
|
||||||
use gdk::*;
|
use gdk::*;
|
||||||
use gio::prelude::*;
|
use gio::prelude::*;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{Adjustment, Application, ApplicationWindow, Button, Scale};
|
use gtk::{Application, ApplicationWindow};
|
||||||
use regex::Regex;
|
use std::{collections::HashMap, process::Command};
|
||||||
use std::{collections::HashMap, str::FromStr};
|
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
const CMD_STRING_PLACEHODLER: &str = "{}";
|
||||||
let application = Application::new(Some("de.elkowar.eww"), Default::default())
|
|
||||||
.expect("failed to initialize GTK application");
|
|
||||||
|
|
||||||
application.connect_activate(|app| {
|
const EXAMPLE_CONFIG: &str = r#"{
|
||||||
let window = ApplicationWindow::new(app);
|
widgets: {
|
||||||
window.set_title("Eww");
|
some_widget: {
|
||||||
window.set_wmclass("noswallow", "noswallow");
|
structure: {
|
||||||
window.set_type_hint(gdk::WindowTypeHint::Dock);
|
|
||||||
window.set_position(gtk::WindowPosition::Center);
|
|
||||||
window.set_keep_above(true);
|
|
||||||
|
|
||||||
let element = config::parse_element_use(
|
|
||||||
config::parse_hocon(
|
|
||||||
r#"{
|
|
||||||
layout_horizontal: {
|
layout_horizontal: {
|
||||||
|
class: "container",
|
||||||
children: [
|
children: [
|
||||||
"hi",
|
"hi",
|
||||||
{ button: { children: "click me you" } }
|
{ button: { children: "click me you" } }
|
||||||
{ slider: {} }
|
{ slider: { value: 12, min: 0, max: 50, onchange: "notify-send 'changed' {}" } }
|
||||||
"hu"
|
"hu"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}"#,
|
}
|
||||||
)
|
}
|
||||||
.unwrap(),
|
},
|
||||||
)
|
windows: {
|
||||||
.unwrap();
|
main_window: {
|
||||||
|
pos.x: 200
|
||||||
|
pos.y: 1550
|
||||||
|
size.x: 500
|
||||||
|
size.y: 50
|
||||||
|
widget: {
|
||||||
|
some_widget: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(EXAMPLE_CONFIG)?)?;
|
||||||
|
|
||||||
|
let application = Application::new(Some("de.elkowar.eww"), gio::ApplicationFlags::FLAGS_NONE)
|
||||||
|
.expect("failed to initialize GTK application");
|
||||||
|
|
||||||
|
let window_def = eww_config.windows()["main_window"].clone();
|
||||||
|
|
||||||
|
application.connect_activate(move |app| {
|
||||||
|
let app_window = ApplicationWindow::new(app);
|
||||||
|
app_window.set_title("Eww");
|
||||||
|
app_window.set_wmclass("noswallow", "noswallow");
|
||||||
|
app_window.set_type_hint(gdk::WindowTypeHint::Dock);
|
||||||
|
app_window.set_position(gtk::WindowPosition::Center);
|
||||||
|
app_window.set_keep_above(true);
|
||||||
|
app_window.set_default_size(window_def.size.0, window_def.size.1);
|
||||||
|
app_window.set_visual(
|
||||||
|
app_window
|
||||||
|
.get_display()
|
||||||
|
.get_default_screen()
|
||||||
|
.get_rgba_visual()
|
||||||
|
.or_else(|| {
|
||||||
|
app_window
|
||||||
|
.get_display()
|
||||||
|
.get_default_screen()
|
||||||
|
.get_system_visual()
|
||||||
|
})
|
||||||
|
.as_ref(),
|
||||||
|
);
|
||||||
|
|
||||||
|
app_window.fullscreen();
|
||||||
|
|
||||||
let widget_state = WidgetState(HashMap::new());
|
let widget_state = WidgetState(HashMap::new());
|
||||||
|
|
||||||
window.add(&element_to_gtk_widget(&widget_state, &element).unwrap());
|
app_window.add(
|
||||||
|
&element_to_gtk_widget(&eww_config.widgets(), &widget_state, &window_def.widget)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
window.show_all();
|
app_window.show_all();
|
||||||
|
|
||||||
|
let window = app_window.get_window().unwrap();
|
||||||
|
|
||||||
|
window.set_override_redirect(true);
|
||||||
|
window.move_(window_def.position.0, window_def.position.1);
|
||||||
|
window.show();
|
||||||
|
window.raise();
|
||||||
});
|
});
|
||||||
|
|
||||||
application.run(&[]);
|
application.run(&[]);
|
||||||
|
@ -54,17 +98,32 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn element_to_gtk_widget(
|
fn element_to_gtk_widget(
|
||||||
|
widget_definitions: &HashMap<String, config::WidgetDefinition>,
|
||||||
widget_state: &WidgetState,
|
widget_state: &WidgetState,
|
||||||
element: &config::ElementUse,
|
element: &config::ElementUse,
|
||||||
) -> Option<gtk::Widget> {
|
) -> Option<gtk::Widget> {
|
||||||
match element {
|
match element {
|
||||||
config::ElementUse::Text(text) => Some(gtk::Label::new(Some(&text)).upcast()),
|
config::ElementUse::Text(text) => Some(gtk::Label::new(Some(&text)).upcast()),
|
||||||
config::ElementUse::Widget(widget) => widget_use_to_gtk_container(widget_state, &widget)
|
config::ElementUse::Widget(widget) => {
|
||||||
.or(widget_use_to_gtk_widget(widget_state, &widget)),
|
let gtk_widget =
|
||||||
|
widget_use_to_gtk_container(widget_definitions, widget_state, &widget).or(
|
||||||
|
widget_use_to_gtk_widget(widget_definitions, widget_state, &widget),
|
||||||
|
)?;
|
||||||
|
if let Some(css_class) = widget
|
||||||
|
.attrs
|
||||||
|
.get("class")
|
||||||
|
.and_then(config::AttrValue::as_string)
|
||||||
|
{
|
||||||
|
gtk_widget.get_style_context().add_class(css_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(gtk_widget)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn widget_use_to_gtk_container(
|
fn widget_use_to_gtk_container(
|
||||||
|
widget_definitions: &HashMap<String, config::WidgetDefinition>,
|
||||||
widget_state: &WidgetState,
|
widget_state: &WidgetState,
|
||||||
widget: &config::WidgetUse,
|
widget: &config::WidgetUse,
|
||||||
) -> Option<gtk::Widget> {
|
) -> Option<gtk::Widget> {
|
||||||
|
@ -75,33 +134,63 @@ fn widget_use_to_gtk_container(
|
||||||
};
|
};
|
||||||
|
|
||||||
for child in &widget.children {
|
for child in &widget.children {
|
||||||
container_widget.add(&element_to_gtk_widget(widget_state, child)?);
|
container_widget.add(&element_to_gtk_widget(
|
||||||
|
widget_definitions,
|
||||||
|
widget_state,
|
||||||
|
child,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
Some(container_widget.upcast())
|
Some(container_widget.upcast())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn widget_use_to_gtk_widget(
|
fn widget_use_to_gtk_widget(
|
||||||
widget_state: &WidgetState,
|
widget_definitions: &HashMap<String, config::WidgetDefinition>,
|
||||||
|
state: &WidgetState,
|
||||||
widget: &config::WidgetUse,
|
widget: &config::WidgetUse,
|
||||||
) -> Option<gtk::Widget> {
|
) -> Option<gtk::Widget> {
|
||||||
let new_widget: gtk::Widget = match widget.name.as_str() {
|
let new_widget: gtk::Widget = match widget.name.as_str() {
|
||||||
"slider" => {
|
"slider" => {
|
||||||
let slider_value: f64 = widget_state.resolve(widget.attrs.get("value")?)?;
|
let slider_value: f64 = state.resolve(widget.attrs.get("value")?)?.as_f64()?;
|
||||||
|
let slider_min: Option<f64> =
|
||||||
|
try { state.resolve(widget.attrs.get("min")?)?.as_f64()? };
|
||||||
|
let slider_min = slider_min.unwrap_or(0f64);
|
||||||
|
let slider_max: Option<f64> =
|
||||||
|
try { state.resolve(widget.attrs.get("max")?)?.as_f64()? };
|
||||||
|
let slider_max = slider_max.unwrap_or(100f64);
|
||||||
|
|
||||||
gtk::Scale::new(
|
let on_change: Option<String> = try {
|
||||||
|
state
|
||||||
|
.resolve(widget.attrs.get("onchange")?)?
|
||||||
|
.as_string()?
|
||||||
|
.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let scale = gtk::Scale::new(
|
||||||
gtk::Orientation::Horizontal,
|
gtk::Orientation::Horizontal,
|
||||||
Some(>k::Adjustment::new(
|
Some(>k::Adjustment::new(
|
||||||
slider_value,
|
slider_value,
|
||||||
0.0,
|
slider_min,
|
||||||
100.0,
|
slider_max,
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
)),
|
)),
|
||||||
)
|
);
|
||||||
.upcast()
|
scale.set_property("draw-value", &false.to_value()).ok()?;
|
||||||
|
|
||||||
|
if let Some(on_change) = on_change {
|
||||||
|
scale.connect_value_changed(move |scale| {
|
||||||
|
run_command(&on_change, scale.get_value());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scale.upcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
name if widget_definitions.contains_key(name) => {
|
||||||
|
let def = &widget_definitions[name];
|
||||||
|
element_to_gtk_widget(widget_definitions, state, &def.structure)?
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(new_widget)
|
Some(new_widget)
|
||||||
|
@ -110,31 +199,20 @@ fn widget_use_to_gtk_widget(
|
||||||
struct WidgetState(HashMap<String, config::AttrValue>);
|
struct WidgetState(HashMap<String, config::AttrValue>);
|
||||||
|
|
||||||
impl WidgetState {
|
impl WidgetState {
|
||||||
pub fn resolve<T>(&self, value: &config::AttrValue) -> Option<String>
|
pub fn resolve(&self, value: &config::AttrValue) -> Option<config::AttrValue> {
|
||||||
where
|
if let config::AttrValue::VarRef(name) = value {
|
||||||
T: FromStr,
|
// TODO REEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
|
||||||
{
|
self.0.get(name).cloned()
|
||||||
let var_pattern: Regex = Regex::new(r"\$\$\{(.*)\}").unwrap();
|
} else {
|
||||||
let config::AttrValue(value) = value;
|
Some(value.clone())
|
||||||
|
|
||||||
let mut missing_var: Option<String> = None;
|
|
||||||
var_pattern.replace_all(value, |caps: ®ex::Captures| {
|
|
||||||
self.lookup_full::<T>(&caps[1]).unwrap_or_else(|| {
|
|
||||||
missing_var = Some(caps[1].to_string());
|
|
||||||
"missing".to_string()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO REEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEe
|
|
||||||
|
|
||||||
unimplemented!();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn lookup_full<T>(&self, key: &str) -> Option<String>
|
fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) {
|
||||||
where
|
let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg));
|
||||||
T: FromStr,
|
if let Err(e) = Command::new("bash").arg("-c").arg(cmd).output() {
|
||||||
{
|
eprintln!("{}", e);
|
||||||
self.resolve::<T>(self.0.get(key)?)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue