use indexmap::IndexMap;
use nemo_plugin_api::PluginValue;
pub trait Builder {
fn build(self) -> PluginValue;
}
pub struct LayoutBuilder {
attributes: IndexMap<String, PluginValue>,
children: Option<IndexMap<String, PluginValue>>,
}
impl LayoutBuilder {
pub fn new(component_type: impl Into<String>) -> Self {
let mut attributes = IndexMap::new();
attributes.insert(
"type".to_string(),
PluginValue::String(component_type.into()),
);
Self {
attributes,
children: None,
}
}
pub fn attr(mut self, key: impl Into<String>, value: impl Into<PluginValue>) -> Self {
self.attributes.insert(key.into(), value.into());
self
}
pub fn attrs(mut self, attrs: &[(&str, PluginValue)]) -> Self {
for (k, v) in attrs {
self.attributes.insert(k.to_string(), v.clone());
}
self
}
pub fn child(mut self, id: impl Into<String>, child: impl Builder) -> Self {
let children = self.children.get_or_insert_with(IndexMap::new);
children.insert(id.into(), child.build());
self
}
pub fn children(mut self, children: impl IntoIterator<Item = (String, PluginValue)>) -> Self {
let child_map = self.children.get_or_insert_with(IndexMap::new);
for (id, value) in children {
child_map.insert(id, value);
}
self
}
pub fn with_children(mut self, children: IndexMap<String, PluginValue>) -> Self {
self.children = Some(children);
self
}
}
impl Builder for LayoutBuilder {
fn build(mut self) -> PluginValue {
if let Some(children) = self.children {
self.attributes
.insert("component".to_string(), PluginValue::Object(children));
}
PluginValue::Object(self.attributes)
}
}
impl LayoutBuilder {
pub fn width(self, width: i64) -> Self {
self.attr("width", PluginValue::Integer(width))
}
pub fn height(self, height: i64) -> Self {
self.attr("height", PluginValue::Integer(height))
}
pub fn padding(self, padding: i64) -> Self {
self.attr("padding", PluginValue::Integer(padding))
}
pub fn margin(self, margin: i64) -> Self {
self.attr("margin", PluginValue::Integer(margin))
}
pub fn border(self, border: i64) -> Self {
self.attr("border", PluginValue::Integer(border))
}
pub fn border_color(self, color: impl Into<String>) -> Self {
self.attr("border_color", PluginValue::String(color.into()))
}
pub fn bg_color(self, color: impl Into<String>) -> Self {
self.attr("bg_color", PluginValue::String(color.into()))
}
pub fn shadow(self, shadow: impl Into<String>) -> Self {
self.attr("shadow", PluginValue::String(shadow.into()))
}
pub fn bind(self, property: impl Into<String>, path: impl Into<String>) -> Self {
let bind_key = format!("bind_{}", property.into());
self.attr(bind_key, PluginValue::String(path.into()))
}
pub fn on(self, event: impl Into<String>, handler: impl Into<String>) -> Self {
let event_key = format!("on_{}", event.into());
self.attr(event_key, PluginValue::String(handler.into()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_layout_builder_basic() {
let layout = LayoutBuilder::new("button")
.attr("label", PluginValue::String("Click".into()))
.build();
match layout {
PluginValue::Object(map) => {
assert_eq!(map.get("type"), Some(&PluginValue::String("button".into())));
assert_eq!(map.get("label"), Some(&PluginValue::String("Click".into())));
}
_ => panic!("Expected Object"),
}
}
#[test]
fn test_layout_builder_with_children() {
let child = LayoutBuilder::new("label")
.attr("text", PluginValue::String("Child".into()))
.build();
let mut children = IndexMap::new();
children.insert("child1".to_string(), child);
let layout = LayoutBuilder::new("panel").with_children(children).build();
match layout {
PluginValue::Object(map) => {
assert!(map.contains_key("component"));
}
_ => panic!("Expected Object"),
}
}
#[test]
fn test_layout_builder_common_attrs() {
let layout = LayoutBuilder::new("panel")
.width(100)
.height(200)
.padding(10)
.margin(5)
.border(2)
.build();
match layout {
PluginValue::Object(map) => {
assert_eq!(map.get("width"), Some(&PluginValue::Integer(100)));
assert_eq!(map.get("height"), Some(&PluginValue::Integer(200)));
assert_eq!(map.get("padding"), Some(&PluginValue::Integer(10)));
assert_eq!(map.get("margin"), Some(&PluginValue::Integer(5)));
assert_eq!(map.get("border"), Some(&PluginValue::Integer(2)));
}
_ => panic!("Expected Object"),
}
}
#[test]
fn test_event_handler() {
let layout = LayoutBuilder::new("button")
.on("click", "handle_click")
.build();
match layout {
PluginValue::Object(map) => {
assert_eq!(
map.get("on_click"),
Some(&PluginValue::String("handle_click".into()))
);
}
_ => panic!("Expected Object"),
}
}
#[test]
fn test_data_binding() {
let layout = LayoutBuilder::new("label")
.bind("text", "data.message")
.build();
match layout {
PluginValue::Object(map) => {
assert_eq!(
map.get("bind_text"),
Some(&PluginValue::String("data.message".into()))
);
}
_ => panic!("Expected Object"),
}
}
}