cleanup
This commit is contained in:
parent
43b431c2dc
commit
1969b6eb56
7 changed files with 413 additions and 269 deletions
36
Cargo.lock
generated
36
Cargo.lock
generated
|
@ -119,9 +119,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.59"
|
version = "1.0.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381"
|
checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -139,6 +139,17 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "0.99.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dcfabdab475c16a93d669dddfc393027803e347d09663f524447f642fbb84ba"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "difference"
|
name = "difference"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -147,9 +158,9 @@ checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
|
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding"
|
name = "encoding"
|
||||||
|
@ -220,11 +231,13 @@ name = "eww"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"derive_more",
|
||||||
"gdk",
|
"gdk",
|
||||||
"gio",
|
"gio",
|
||||||
"glib",
|
"glib",
|
||||||
"gtk",
|
"gtk",
|
||||||
"hocon",
|
"hocon",
|
||||||
|
"maplit",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"regex",
|
"regex",
|
||||||
"try_match",
|
"try_match",
|
||||||
|
@ -452,14 +465,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gio-sys"
|
name = "gio-sys"
|
||||||
version = "0.10.0"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35993626299fbcaa73c0a19be8fdd01c950f9f3d3ac9cb4fb5532b924ab1a5d7"
|
checksum = "5e24fb752f8f5d2cf6bbc2c606fd2bc989c81c5e2fe321ab974d54f8b6344eac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys",
|
||||||
"gobject-sys",
|
"gobject-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -499,9 +513,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glib-sys"
|
name = "glib-sys"
|
||||||
version = "0.10.0"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6cda4af5c2f4507b7a3535b798dca2135293f4bc3a17f399ce244ef15841c4c"
|
checksum = "c7e9b997a66e9a23d073f2b1abb4dbfc3925e0b8952f67efd8d9b6e168e4cdc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
|
@ -618,6 +632,12 @@ version = "0.2.77"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
|
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maplit"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.3.3"
|
version = "2.3.3"
|
||||||
|
|
|
@ -24,3 +24,6 @@ anyhow = "1.0"
|
||||||
|
|
||||||
|
|
||||||
pretty_assertions = "0.6.1"
|
pretty_assertions = "0.6.1"
|
||||||
|
|
||||||
|
derive_more = "0.99"
|
||||||
|
maplit = "1"
|
||||||
|
|
171
src/config/element.rs
Normal file
171
src/config/element.rs
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use hocon_ext::HoconExt;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct WidgetDefinition {
|
||||||
|
pub name: String,
|
||||||
|
pub structure: ElementUse,
|
||||||
|
pub size: Option<(i32, i32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetDefinition {
|
||||||
|
pub fn parse_hocon(name: String, hocon: &Hocon) -> Result<Self> {
|
||||||
|
let definition = hocon.as_hash()?;
|
||||||
|
let structure = definition
|
||||||
|
.get("structure")
|
||||||
|
.cloned()
|
||||||
|
.context("structure must be set in widget definition")
|
||||||
|
.and_then(ElementUse::parse_hocon)?;
|
||||||
|
|
||||||
|
Ok(WidgetDefinition {
|
||||||
|
name,
|
||||||
|
structure,
|
||||||
|
size: try {
|
||||||
|
(
|
||||||
|
definition.get("size_x")?.as_i64()? as i32,
|
||||||
|
definition.get("size_y")?.as_i64()? as i32,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum ElementUse {
|
||||||
|
Widget(WidgetUse),
|
||||||
|
Text(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElementUse {
|
||||||
|
pub fn parse_hocon(hocon: Hocon) -> Result<Self> {
|
||||||
|
match hocon {
|
||||||
|
Hocon::String(s) => Ok(ElementUse::Text(s)),
|
||||||
|
Hocon::Hash(hash) => WidgetUse::parse_hocon_hash(hash).map(ElementUse::Widget),
|
||||||
|
_ => Err(anyhow!("{:?} is not a valid element", hocon)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
|
pub struct WidgetUse {
|
||||||
|
pub name: String,
|
||||||
|
pub children: Vec<ElementUse>,
|
||||||
|
pub attrs: HashMap<String, AttrValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetUse {
|
||||||
|
pub fn new(name: String, children: Vec<ElementUse>) -> Self {
|
||||||
|
WidgetUse {
|
||||||
|
name,
|
||||||
|
children,
|
||||||
|
attrs: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_hocon_hash(data: HashMap<String, Hocon>) -> Result<WidgetUse> {
|
||||||
|
let (widget_name, widget_config) = data.into_iter().next().unwrap();
|
||||||
|
let widget_config = widget_config.as_hash().unwrap();
|
||||||
|
|
||||||
|
// TODO allow for `layout_horizontal: [ elements ]` shorthand
|
||||||
|
|
||||||
|
let children = match &widget_config.get("children") {
|
||||||
|
Some(Hocon::String(text)) => Ok(vec![ElementUse::Text(text.to_string())]),
|
||||||
|
Some(Hocon::Array(children)) => children
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(ElementUse::parse_hocon)
|
||||||
|
.collect::<Result<Vec<_>>>(),
|
||||||
|
None => Ok(Vec::new()),
|
||||||
|
_ => Err(anyhow!(
|
||||||
|
"children must be either a list of elements or a string, but was {:?}"
|
||||||
|
)),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let attrs = widget_config
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(key, value)| Some((key.to_lowercase(), AttrValue::try_from(value).ok()?)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(WidgetUse {
|
||||||
|
name: widget_name.to_string(),
|
||||||
|
children,
|
||||||
|
attrs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WidgetUse> for ElementUse {
|
||||||
|
fn from(other: WidgetUse) -> ElementUse {
|
||||||
|
ElementUse::Widget(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use maplit::hashmap;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_text() {
|
||||||
|
assert_eq!(
|
||||||
|
ElementUse::parse_hocon(Hocon::String("hi".to_string())).unwrap(),
|
||||||
|
ElementUse::Text("hi".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_widget_use() {
|
||||||
|
let input_complex = r#"{
|
||||||
|
widget_name: {
|
||||||
|
value: "test"
|
||||||
|
children: [
|
||||||
|
{ child: {} }
|
||||||
|
{ child: {} }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
let expected = WidgetUse {
|
||||||
|
name: "widget_name".to_string(),
|
||||||
|
children: vec![
|
||||||
|
ElementUse::Widget(WidgetUse::new("child".to_string(), vec![])),
|
||||||
|
ElementUse::Widget(WidgetUse::new("child".to_string(), vec![])),
|
||||||
|
],
|
||||||
|
attrs: hashmap! { "value".to_string() => AttrValue::Concrete(PrimitiveValue::String("test".to_string()))},
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
WidgetUse::parse_hocon_hash(
|
||||||
|
parse_hocon(input_complex)
|
||||||
|
.unwrap()
|
||||||
|
.as_hash()
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_widget_definition() {
|
||||||
|
let input_complex = r#"{
|
||||||
|
structure: { foo: {} }
|
||||||
|
}"#;
|
||||||
|
let expected = WidgetDefinition {
|
||||||
|
name: "widget_name".to_string(),
|
||||||
|
structure: ElementUse::Widget(WidgetUse::new("foo".to_string(), vec![])),
|
||||||
|
size: None,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
WidgetDefinition::parse_hocon(
|
||||||
|
"widget_name".to_string(),
|
||||||
|
&parse_hocon(input_complex).unwrap()
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,24 +1,23 @@
|
||||||
|
use anyhow::*;
|
||||||
use hocon::Hocon;
|
use hocon::Hocon;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub trait HoconExt: Sized {
|
pub trait HoconExt: Sized {
|
||||||
fn as_hash(&self) -> Option<&HashMap<String, Self>>;
|
fn as_hash(&self) -> Result<&HashMap<String, Self>>;
|
||||||
fn as_array(&self) -> Option<&Vec<Self>>;
|
fn as_array(&self) -> Result<&Vec<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HoconExt for Hocon {
|
impl HoconExt for Hocon {
|
||||||
// TODO take owned self here?
|
fn as_hash(&self) -> Result<&HashMap<String, Self>> {
|
||||||
|
|
||||||
fn as_hash(&self) -> Option<&HashMap<String, Self>> {
|
|
||||||
match self {
|
match self {
|
||||||
Hocon::Hash(x) => Some(x),
|
Hocon::Hash(x) => Ok(x),
|
||||||
_ => None,
|
_ => Err(anyhow!("as_hash called with {:?}", self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn as_array(&self) -> Option<&Vec<Self>> {
|
fn as_array(&self) -> Result<&Vec<Self>> {
|
||||||
match self {
|
match self {
|
||||||
Hocon::Array(x) => Some(x),
|
Hocon::Array(x) => Ok(x),
|
||||||
_ => None,
|
_ => Err(anyhow!("as_array called with {:?}", self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +1,66 @@
|
||||||
|
use crate::value::PrimitiveValue;
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
|
use element::*;
|
||||||
use hocon::*;
|
use hocon::*;
|
||||||
use hocon_ext::HoconExt;
|
use hocon_ext::HoconExt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use try_match::try_match;
|
use try_match::try_match;
|
||||||
|
|
||||||
|
pub mod element;
|
||||||
pub mod hocon_ext;
|
pub mod hocon_ext;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EwwConfig {
|
pub struct EwwConfig {
|
||||||
widgets: HashMap<String, WidgetDefinition>,
|
widgets: HashMap<String, WidgetDefinition>,
|
||||||
windows: HashMap<String, EwwWindowDefinition>,
|
windows: HashMap<String, EwwWindowDefinition>,
|
||||||
default_vars: HashMap<String, AttrValue>,
|
default_vars: HashMap<String, PrimitiveValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
macro_rules! try_type {
|
||||||
|
($typ:ty; $code:expr) => {{
|
||||||
|
let x: $typ = try { $code };
|
||||||
|
x
|
||||||
|
}};
|
||||||
|
($typ:ty; $code:block) => {{
|
||||||
|
let x: $typ = try { $code };
|
||||||
|
x
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EwwConfig {
|
impl EwwConfig {
|
||||||
pub fn from_hocon(hocon: &Hocon) -> Result<EwwConfig> {
|
pub fn from_hocon(hocon: &Hocon) -> Result<EwwConfig> {
|
||||||
let data = hocon
|
let data = hocon.as_hash()?;
|
||||||
.as_hash()
|
|
||||||
.context("eww config has to be a map structure")?;
|
|
||||||
|
|
||||||
Ok(EwwConfig {
|
let widgets = data
|
||||||
widgets: data
|
|
||||||
.get("widgets")
|
.get("widgets")
|
||||||
.context("widgets need to be provided")?
|
.context("widgets field missing")?
|
||||||
.as_hash()
|
.as_hash()?
|
||||||
.context("widgets need to be a map")?
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, def)| Ok((name.clone(), parse_widget_definition(name.clone(), def)?)))
|
.map(|(n, def)| Ok((n.clone(), WidgetDefinition::parse_hocon(n.clone(), def)?)))
|
||||||
.collect::<Result<HashMap<String, WidgetDefinition>>>()?,
|
.collect::<Result<_>>()?;
|
||||||
windows: data
|
|
||||||
|
let windows = data
|
||||||
.get("windows")
|
.get("windows")
|
||||||
.context("windows need to be provided")?
|
.context("windows field missing")?
|
||||||
.as_hash()
|
.as_hash()?
|
||||||
.context("windows need to be a map")?
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, def)| Ok((name.clone(), EwwWindowDefinition::from_hocon(def)?)))
|
.map(|(name, def)| Ok((name.clone(), EwwWindowDefinition::from_hocon(def)?)))
|
||||||
.collect::<Result<HashMap<String, EwwWindowDefinition>>>()?,
|
.collect::<Result<_>>()?;
|
||||||
default_vars: data
|
|
||||||
|
let default_vars = data
|
||||||
.get("default_vars")
|
.get("default_vars")
|
||||||
.unwrap_or(&Hocon::Hash(HashMap::new()))
|
.unwrap_or(&Hocon::Hash(HashMap::new()))
|
||||||
.as_hash()
|
.as_hash()?
|
||||||
.context("default_vars needs to be a map")?
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, def)| Ok((name.clone(), AttrValue::try_from(def)?)))
|
.map(|(name, def)| Ok((name.clone(), PrimitiveValue::try_from(def)?)))
|
||||||
.collect::<Result<HashMap<_, _>>>()?,
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
|
Ok(EwwConfig {
|
||||||
|
widgets,
|
||||||
|
windows,
|
||||||
|
default_vars,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +70,7 @@ impl EwwConfig {
|
||||||
pub fn get_windows(&self) -> &HashMap<String, EwwWindowDefinition> {
|
pub fn get_windows(&self) -> &HashMap<String, EwwWindowDefinition> {
|
||||||
&self.windows
|
&self.windows
|
||||||
}
|
}
|
||||||
pub fn get_default_vars(&self) -> &HashMap<String, AttrValue> {
|
pub fn get_default_vars(&self) -> &HashMap<String, PrimitiveValue> {
|
||||||
&self.default_vars
|
&self.default_vars
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,19 +89,19 @@ impl EwwWindowDefinition {
|
||||||
.context("window config has to be a map structure")?;
|
.context("window config has to be a map structure")?;
|
||||||
let position: Option<_> = try {
|
let position: Option<_> = try {
|
||||||
(
|
(
|
||||||
data.get("pos")?.as_hash()?.get("x")?.as_i64()? as i32,
|
data.get("pos")?.as_hash().ok()?.get("x")?.as_i64()? as i32,
|
||||||
data.get("pos")?.as_hash()?.get("y")?.as_i64()? as i32,
|
data.get("pos")?.as_hash().ok()?.get("y")?.as_i64()? as i32,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let size: Option<_> = try {
|
let size: Option<_> = try {
|
||||||
(
|
(
|
||||||
data.get("size")?.as_hash()?.get("x")?.as_i64()? as i32,
|
data.get("size")?.as_hash().ok()?.get("x")?.as_i64()? as i32,
|
||||||
data.get("size")?.as_hash()?.get("y")?.as_i64()? as i32,
|
data.get("size")?.as_hash().ok()?.get("y")?.as_i64()? as i32,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let element =
|
let element =
|
||||||
parse_element_use(data.get("widget").context("no widget use given")?.clone())?;
|
ElementUse::parse_hocon(data.get("widget").context("no widget use given")?.clone())?;
|
||||||
|
|
||||||
Ok(EwwWindowDefinition {
|
Ok(EwwWindowDefinition {
|
||||||
position: position.context("pos.x and pos.y need to be set")?,
|
position: position.context("pos.x and pos.y need to be set")?,
|
||||||
|
@ -97,24 +113,34 @@ impl EwwWindowDefinition {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum AttrValue {
|
pub enum AttrValue {
|
||||||
String(String),
|
Concrete(PrimitiveValue),
|
||||||
Number(f64),
|
|
||||||
Boolean(bool),
|
|
||||||
VarRef(String),
|
VarRef(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttrValue {
|
impl AttrValue {
|
||||||
pub fn as_string(&self) -> Option<&String> {
|
pub fn as_string(&self) -> Result<&String> {
|
||||||
try_match!(AttrValue::String(x) = self).ok()
|
try_match!(AttrValue::Concrete(x) = self)
|
||||||
|
.map_err(|e| anyhow!("{:?} is not a string", e))?
|
||||||
|
.as_string()
|
||||||
}
|
}
|
||||||
pub fn as_f64(&self) -> Option<f64> {
|
pub fn as_f64(&self) -> Result<f64> {
|
||||||
try_match!(AttrValue::Number(x) = self => *x).ok()
|
try_match!(AttrValue::Concrete(x) = self)
|
||||||
|
.map_err(|e| anyhow!("{:?} is not an f64", e))?
|
||||||
|
.as_f64()
|
||||||
}
|
}
|
||||||
pub fn as_bool(&self) -> Option<bool> {
|
pub fn as_bool(&self) -> Result<bool> {
|
||||||
try_match!(AttrValue::Boolean(x) = self => *x).ok()
|
try_match!(AttrValue::Concrete(x) = self)
|
||||||
|
.map_err(|e| anyhow!("{:?} is not a bool", e))?
|
||||||
|
.as_bool()
|
||||||
}
|
}
|
||||||
pub fn as_var_ref(&self) -> Option<&String> {
|
pub fn as_var_ref(&self) -> Result<&String> {
|
||||||
try_match!(AttrValue::VarRef(x) = self).ok()
|
try_match!(AttrValue::VarRef(x) = self).map_err(|e| anyhow!("{:?} is not a VarRef", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PrimitiveValue> for AttrValue {
|
||||||
|
fn from(value: PrimitiveValue) -> Self {
|
||||||
|
AttrValue::Concrete(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,165 +151,15 @@ impl std::convert::TryFrom<&Hocon> for AttrValue {
|
||||||
Hocon::String(s) if s.starts_with("$$") => {
|
Hocon::String(s) if s.starts_with("$$") => {
|
||||||
AttrValue::VarRef(s.trim_start_matches("$$").to_string())
|
AttrValue::VarRef(s.trim_start_matches("$$").to_string())
|
||||||
}
|
}
|
||||||
Hocon::String(s) => AttrValue::String(s.to_string()),
|
Hocon::String(s) => AttrValue::Concrete(PrimitiveValue::String(s.clone())),
|
||||||
Hocon::Integer(n) => AttrValue::Number(*n as f64),
|
Hocon::Integer(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)),
|
||||||
Hocon::Real(n) => AttrValue::Number(*n as f64),
|
Hocon::Real(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)),
|
||||||
Hocon::Boolean(b) => AttrValue::Boolean(*b),
|
Hocon::Boolean(b) => AttrValue::Concrete(PrimitiveValue::Boolean(*b)),
|
||||||
_ => return Err(anyhow!("cannot convert {} to config::AttrValue")),
|
_ => return Err(anyhow!("cannot convert {:?} to config::AttrValue", &value)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct WidgetDefinition {
|
|
||||||
pub name: String,
|
|
||||||
pub structure: ElementUse,
|
|
||||||
pub size: Option<(i32, i32)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum ElementUse {
|
|
||||||
Widget(WidgetUse),
|
|
||||||
Text(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct WidgetUse {
|
|
||||||
pub name: String,
|
|
||||||
pub children: Vec<ElementUse>,
|
|
||||||
pub attrs: HashMap<String, AttrValue>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WidgetUse {
|
|
||||||
pub fn new(name: String, children: Vec<ElementUse>) -> Self {
|
|
||||||
WidgetUse {
|
|
||||||
name,
|
|
||||||
children,
|
|
||||||
attrs: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<WidgetUse> for ElementUse {
|
|
||||||
fn from(other: WidgetUse) -> ElementUse {
|
|
||||||
ElementUse::Widget(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_widget_definition(name: String, hocon: &Hocon) -> Result<WidgetDefinition> {
|
|
||||||
let definition = hocon
|
|
||||||
.as_hash()
|
|
||||||
.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 {
|
|
||||||
name,
|
|
||||||
structure,
|
|
||||||
size: try {
|
|
||||||
(
|
|
||||||
definition.get("size_x")?.as_i64()? as i32,
|
|
||||||
definition.get("size_y")?.as_i64()? as i32,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_element_use(hocon: Hocon) -> Result<ElementUse> {
|
|
||||||
match hocon {
|
|
||||||
Hocon::String(s) => Ok(ElementUse::Text(s)),
|
|
||||||
Hocon::Hash(hash) => parse_widget_use(hash).map(ElementUse::Widget),
|
|
||||||
_ => Err(anyhow!("{:?} is not a valid element", hocon)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_widget_use(data: HashMap<String, Hocon>) -> Result<WidgetUse> {
|
|
||||||
let (widget_name, widget_config) = data.into_iter().next().unwrap();
|
|
||||||
let widget_config = widget_config.as_hash().unwrap();
|
|
||||||
|
|
||||||
// TODO allow for `layout_horizontal: [ elements ]` shorthand
|
|
||||||
|
|
||||||
let children = match &widget_config.get("children") {
|
|
||||||
Some(Hocon::String(text)) => Ok(vec![ElementUse::Text(text.to_string())]),
|
|
||||||
Some(Hocon::Array(children)) => children
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.map(parse_element_use)
|
|
||||||
.collect::<Result<Vec<_>>>(),
|
|
||||||
None => Ok(Vec::new()),
|
|
||||||
_ => Err(anyhow!(
|
|
||||||
"children must be either a list of elements or a string, but was {:?}"
|
|
||||||
)),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let attrs: HashMap<String, AttrValue> = widget_config
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(key, value)| Some((key.to_lowercase(), AttrValue::try_from(value).ok()?)))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(WidgetUse {
|
|
||||||
name: widget_name.to_string(),
|
|
||||||
children,
|
|
||||||
attrs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_hocon(s: &str) -> Result<Hocon> {
|
pub fn parse_hocon(s: &str) -> Result<Hocon> {
|
||||||
Ok(HoconLoader::new().load_str(s)?.hocon()?)
|
Ok(HoconLoader::new().load_str(s)?.hocon()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const EXAMPLE_CONFIG: &'static str = r#"{
|
|
||||||
name: "example_widget"
|
|
||||||
structure {
|
|
||||||
layout_horizontal {
|
|
||||||
children: [
|
|
||||||
{ text { children: "hi", color: "red" } }
|
|
||||||
{ text: {} }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}"#;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse() {
|
|
||||||
assert_eq!(
|
|
||||||
parse_element_use(Hocon::String("hi".to_string())).unwrap(),
|
|
||||||
ElementUse::Text("hi".to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_parse_widget_definition() {
|
|
||||||
// let expected = WidgetDefinition {
|
|
||||||
// name: "example_widget".to_string(),
|
|
||||||
// structure: ElementUse::Widget(WidgetUse {
|
|
||||||
// name: "layout_horizontal".to_string(),
|
|
||||||
// attrs: HashMap::new(),
|
|
||||||
// children: vec![
|
|
||||||
// ElementUse::Widget(WidgetUse::new(
|
|
||||||
// "text".to_string(),
|
|
||||||
// vec![ElementUse::Text("hi".to_string())],
|
|
||||||
// )),
|
|
||||||
// ElementUse::Widget(WidgetUse::new("text".to_string(), vec![])),
|
|
||||||
// ],
|
|
||||||
// }),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// let parsed_hocon = parse_hocon("{ text: { children: \"hi\" } }").unwrap();
|
|
||||||
// assert_eq!(
|
|
||||||
// parse_element_use(parsed_hocon).unwrap(),
|
|
||||||
// ElementUse::Widget(WidgetUse::new(
|
|
||||||
// "text".to_string(),
|
|
||||||
// vec![ElementUse::Text("hi".to_string())]
|
|
||||||
// ))
|
|
||||||
// );
|
|
||||||
// assert_eq!(parse_widget_definition(EXAMPLE_CONFIG).unwrap(), expected);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
109
src/main.rs
109
src/main.rs
|
@ -10,8 +10,13 @@ use gtk::{Application, ApplicationWindow};
|
||||||
use std::{collections::HashMap, process::Command};
|
use std::{collections::HashMap, process::Command};
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod value;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
|
|
||||||
|
use config::element;
|
||||||
|
use config::AttrValue;
|
||||||
|
use value::PrimitiveValue;
|
||||||
|
|
||||||
const CMD_STRING_PLACEHODLER: &str = "{}";
|
const CMD_STRING_PLACEHODLER: &str = "{}";
|
||||||
|
|
||||||
const EXAMPLE_CONFIG: &str = r#"{
|
const EXAMPLE_CONFIG: &str = r#"{
|
||||||
|
@ -29,6 +34,13 @@ const EXAMPLE_CONFIG: &str = r#"{
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
structure: {
|
||||||
|
some_widget: {
|
||||||
|
some_value: "$$ooph"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
default_vars: {
|
default_vars: {
|
||||||
|
@ -41,8 +53,8 @@ const EXAMPLE_CONFIG: &str = r#"{
|
||||||
size.x: 500
|
size.x: 500
|
||||||
size.y: 50
|
size.y: 50
|
||||||
widget: {
|
widget: {
|
||||||
some_widget: {
|
test: {
|
||||||
some_value: "$$ree"
|
ooph: "$$ree"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,10 +71,16 @@ macro_rules! build {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum MuhhMsg {
|
enum MuhhMsg {
|
||||||
UpdateValue(String, config::AttrValue),
|
UpdateValue(String, PrimitiveValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() {
|
||||||
|
if let Err(e) = try_main() {
|
||||||
|
eprintln!("{:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_main() -> Result<()> {
|
||||||
let eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(EXAMPLE_CONFIG)?)?;
|
let eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(EXAMPLE_CONFIG)?)?;
|
||||||
dbg!(&eww_config);
|
dbg!(&eww_config);
|
||||||
|
|
||||||
|
@ -137,35 +155,35 @@ fn event_loop(sender: glib::Sender<MuhhMsg>) {
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
loop {
|
loop {
|
||||||
x += 1;
|
x += 1;
|
||||||
std::thread::sleep_ms(1000);
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||||
sender
|
sender
|
||||||
.send(MuhhMsg::UpdateValue(
|
.send(MuhhMsg::UpdateValue(
|
||||||
"ree".to_string(),
|
"ree".to_string(),
|
||||||
config::AttrValue::Number(x as f64 * 10.0),
|
PrimitiveValue::Number(x as f64 * 10.0),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn element_to_gtk_thing(
|
fn element_to_gtk_thing(
|
||||||
widget_definitions: &HashMap<String, config::WidgetDefinition>,
|
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
||||||
eww_state: &mut EwwState,
|
eww_state: &mut EwwState,
|
||||||
local_environment: &HashMap<String, config::AttrValue>,
|
local_environment: &HashMap<String, AttrValue>,
|
||||||
element: &config::ElementUse,
|
element: &element::ElementUse,
|
||||||
) -> Option<gtk::Widget> {
|
) -> Option<gtk::Widget> {
|
||||||
match element {
|
match element {
|
||||||
config::ElementUse::Text(text) => Some(gtk::Label::new(Some(&text)).upcast()),
|
element::ElementUse::Text(text) => Some(gtk::Label::new(Some(&text)).upcast()),
|
||||||
config::ElementUse::Widget(widget) => {
|
element::ElementUse::Widget(widget) => {
|
||||||
widget_use_to_gtk_thing(widget_definitions, eww_state, local_environment, widget)
|
widget_use_to_gtk_thing(widget_definitions, eww_state, local_environment, widget)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn widget_use_to_gtk_thing(
|
fn widget_use_to_gtk_thing(
|
||||||
widget_definitions: &HashMap<String, config::WidgetDefinition>,
|
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
||||||
eww_state: &mut EwwState,
|
eww_state: &mut EwwState,
|
||||||
local_environment: &HashMap<String, config::AttrValue>,
|
local_environment: &HashMap<String, AttrValue>,
|
||||||
widget: &config::WidgetUse,
|
widget: &element::WidgetUse,
|
||||||
) -> Option<gtk::Widget> {
|
) -> Option<gtk::Widget> {
|
||||||
let gtk_widget =
|
let gtk_widget =
|
||||||
widget_use_to_gtk_container(widget_definitions, eww_state, &local_environment, &widget)
|
widget_use_to_gtk_container(widget_definitions, eww_state, &local_environment, &widget)
|
||||||
|
@ -178,7 +196,7 @@ fn widget_use_to_gtk_thing(
|
||||||
if let Some(css_class) = widget
|
if let Some(css_class) = widget
|
||||||
.attrs
|
.attrs
|
||||||
.get("class")
|
.get("class")
|
||||||
.and_then(config::AttrValue::as_string)
|
.and_then(|x| AttrValue::as_string(x).ok())
|
||||||
{
|
{
|
||||||
gtk_widget.get_style_context().add_class(css_class);
|
gtk_widget.get_style_context().add_class(css_class);
|
||||||
}
|
}
|
||||||
|
@ -187,10 +205,10 @@ fn widget_use_to_gtk_thing(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn widget_use_to_gtk_container(
|
fn widget_use_to_gtk_container(
|
||||||
widget_definitions: &HashMap<String, config::WidgetDefinition>,
|
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
||||||
eww_state: &mut EwwState,
|
eww_state: &mut EwwState,
|
||||||
local_environment: &HashMap<String, config::AttrValue>,
|
local_environment: &HashMap<String, AttrValue>,
|
||||||
widget: &config::WidgetUse,
|
widget: &element::WidgetUse,
|
||||||
) -> Option<gtk::Widget> {
|
) -> Option<gtk::Widget> {
|
||||||
let container_widget: gtk::Container = match widget.name.as_str() {
|
let container_widget: gtk::Container = match widget.name.as_str() {
|
||||||
"layout_horizontal" => gtk::Box::new(gtk::Orientation::Horizontal, 0).upcast(),
|
"layout_horizontal" => gtk::Box::new(gtk::Orientation::Horizontal, 0).upcast(),
|
||||||
|
@ -210,10 +228,10 @@ fn widget_use_to_gtk_container(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn widget_use_to_gtk_widget(
|
fn widget_use_to_gtk_widget(
|
||||||
widget_definitions: &HashMap<String, config::WidgetDefinition>,
|
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
||||||
eww_state: &mut EwwState,
|
eww_state: &mut EwwState,
|
||||||
local_env: &HashMap<String, config::AttrValue>,
|
local_env: &HashMap<String, AttrValue>,
|
||||||
widget: &config::WidgetUse,
|
widget: &element::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" => {
|
||||||
|
@ -266,18 +284,18 @@ fn widget_use_to_gtk_widget(
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct EwwState {
|
struct EwwState {
|
||||||
on_change_handlers: HashMap<String, Vec<Box<dyn Fn(config::AttrValue) + 'static>>>,
|
on_change_handlers: HashMap<String, Vec<Box<dyn Fn(PrimitiveValue) + 'static>>>,
|
||||||
state: HashMap<String, config::AttrValue>,
|
state: HashMap<String, PrimitiveValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EwwState {
|
impl EwwState {
|
||||||
pub fn from_default_vars(defaults: HashMap<String, config::AttrValue>) -> Self {
|
pub fn from_default_vars(defaults: HashMap<String, PrimitiveValue>) -> Self {
|
||||||
EwwState {
|
EwwState {
|
||||||
state: defaults,
|
state: defaults,
|
||||||
..EwwState::default()
|
..EwwState::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn update_value(&mut self, key: String, value: config::AttrValue) {
|
pub fn update_value(&mut self, key: String, value: PrimitiveValue) {
|
||||||
if let Some(handlers) = self.on_change_handlers.get(&key) {
|
if let Some(handlers) = self.on_change_handlers.get(&key) {
|
||||||
for on_change in handlers {
|
for on_change in handlers {
|
||||||
on_change(value.clone());
|
on_change(value.clone());
|
||||||
|
@ -286,59 +304,70 @@ impl EwwState {
|
||||||
self.state.insert(key, value);
|
self.state.insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve<F: Fn(config::AttrValue) + 'static + Clone>(
|
pub fn resolve<F: Fn(PrimitiveValue) + 'static + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
local_env: &HashMap<String, config::AttrValue>,
|
local_env: &HashMap<String, AttrValue>,
|
||||||
value: &config::AttrValue,
|
value: &AttrValue,
|
||||||
set_value: F,
|
set_value: F,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
dbg!("resolve: ", value);
|
dbg!("resolve: ", value);
|
||||||
if let config::AttrValue::VarRef(name) = value {
|
match value {
|
||||||
|
AttrValue::VarRef(name) => {
|
||||||
if let Some(value) = self.state.get(name).cloned() {
|
if let Some(value) = self.state.get(name).cloned() {
|
||||||
self.on_change_handlers
|
self.on_change_handlers
|
||||||
.entry(name.to_string())
|
.entry(name.to_string())
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(Box::new(set_value.clone()));
|
.push(Box::new(set_value.clone()));
|
||||||
self.resolve(local_env, &value, set_value)
|
self.resolve(local_env, &value.into(), set_value)
|
||||||
} else if let Some(value) = local_env.get(name).cloned() {
|
} else if let Some(value) = local_env.get(name).cloned() {
|
||||||
self.resolve(local_env, &value, set_value)
|
self.resolve(local_env, &value, set_value)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
AttrValue::Concrete(value) => {
|
||||||
set_value(value.clone());
|
set_value(value.clone());
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve_f64<F: Fn(f64) + 'static + Clone>(
|
pub fn resolve_f64<F: Fn(f64) + 'static + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
local_env: &HashMap<String, config::AttrValue>,
|
local_env: &HashMap<String, AttrValue>,
|
||||||
value: &config::AttrValue,
|
value: &AttrValue,
|
||||||
set_value: F,
|
set_value: F,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.resolve(local_env, value, move |x| {
|
self.resolve(local_env, value, move |x| {
|
||||||
x.as_f64().map(|v| set_value(v));
|
if let Err(e) = x.as_f64().map(|v| set_value(v)) {
|
||||||
|
eprintln!("error while resolving value: {}", e);
|
||||||
|
};
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn resolve_bool<F: Fn(bool) + 'static + Clone>(
|
pub fn resolve_bool<F: Fn(bool) + 'static + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
local_env: &HashMap<String, config::AttrValue>,
|
local_env: &HashMap<String, AttrValue>,
|
||||||
value: &config::AttrValue,
|
value: &AttrValue,
|
||||||
set_value: F,
|
set_value: F,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.resolve(local_env, value, move |x| {
|
self.resolve(local_env, value, move |x| {
|
||||||
x.as_bool().map(|v| set_value(v));
|
if let Err(e) = x.as_bool().map(|v| set_value(v)) {
|
||||||
|
eprintln!("error while resolving value: {}", e);
|
||||||
|
};
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn resolve_string<F: Fn(String) + 'static + Clone>(
|
pub fn resolve_string<F: Fn(String) + 'static + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
local_env: &HashMap<String, config::AttrValue>,
|
local_env: &HashMap<String, AttrValue>,
|
||||||
value: &config::AttrValue,
|
value: &AttrValue,
|
||||||
set_value: F,
|
set_value: F,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.resolve(local_env, value, move |x| {
|
self.resolve(local_env, value, move |x| {
|
||||||
x.as_string().map(|s| set_value(s.clone()));
|
if let Err(e) = x.as_string().map(|v| set_value(v.clone())) {
|
||||||
|
eprintln!("error while resolving value: {}", e);
|
||||||
|
};
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
46
src/value.rs
Normal file
46
src/value.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use anyhow::*;
|
||||||
|
use derive_more::From;
|
||||||
|
use hocon::Hocon;
|
||||||
|
use try_match::try_match;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, From)]
|
||||||
|
pub enum PrimitiveValue {
|
||||||
|
String(String),
|
||||||
|
Number(f64),
|
||||||
|
Boolean(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimitiveValue {
|
||||||
|
pub fn as_string(&self) -> Result<&String> {
|
||||||
|
try_match!(PrimitiveValue::String(x) = self).map_err(|x| anyhow!("{:?} is not a string", x))
|
||||||
|
}
|
||||||
|
pub fn as_f64(&self) -> Result<f64> {
|
||||||
|
try_match!(PrimitiveValue::Number(x) = self)
|
||||||
|
.map_err(|x| anyhow!("{:?} is not an f64", x))
|
||||||
|
.map(|&x| x)
|
||||||
|
}
|
||||||
|
pub fn as_bool(&self) -> Result<bool> {
|
||||||
|
try_match!(PrimitiveValue::Boolean(x) = self)
|
||||||
|
.map_err(|x| anyhow!("{:?} is not a bool", x))
|
||||||
|
.map(|&x| x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::TryFrom<&Hocon> for PrimitiveValue {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
fn try_from(value: &Hocon) -> Result<Self> {
|
||||||
|
Ok(match value {
|
||||||
|
Hocon::String(s) if s.starts_with("$$") => {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Tried to use variable reference {} as primitive value",
|
||||||
|
s
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Hocon::String(s) => PrimitiveValue::String(s.to_string()),
|
||||||
|
Hocon::Integer(n) => PrimitiveValue::Number(*n as f64),
|
||||||
|
Hocon::Real(n) => PrimitiveValue::Number(*n as f64),
|
||||||
|
Hocon::Boolean(b) => PrimitiveValue::Boolean(*b),
|
||||||
|
_ => return Err(anyhow!("cannot convert {} to config::PrimitiveValue")),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue