use ::{Item, ItemType, Modifier, Icon, ModifierData};
use serde_json as json;
use serde_json::value::Value;
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;
pub fn write_items<W: Write>(w: W, items: &[Item]) -> io::Result<()> {
Builder::with_items(items).write(w)
}
#[derive(Clone,Debug,Default)]
pub struct Builder<'a> {
pub items: &'a [Item<'a>],
pub variables: HashMap<&'a str, &'a str>
}
impl<'a> Builder<'a> {
pub fn new() -> Builder<'a> {
Builder { items: &[], variables: HashMap::new() }
}
pub fn with_items(items: &'a [Item]) -> Builder<'a> {
Builder { items, variables: HashMap::new() }
}
pub fn write<W: Write>(self, mut w: W) -> io::Result<()> {
write!(&mut w, "{}", self.into_json())?;
w.flush()
}
pub fn into_json(self) -> Value {
let mut root = json::Map::new();
root.insert("items".to_string(), Value::Array(self.items.into_iter()
.map(|x| x.to_json())
.collect()));
let mut iter = self.variables.into_iter();
if let Some(first) = iter.next() {
let mut vars = json::Map::new();
vars.insert(first.0.into(), Value::String(first.1.into()));
for elt in iter {
vars.insert(elt.0.into(), Value::String(elt.1.into()));
}
root.insert("variables".to_owned(), Value::Object(vars));
}
Value::Object(root)
}
pub fn items(mut self, items: &'a [Item]) -> Builder<'a> {
self.set_items(items);
self
}
pub fn variables(mut self, variables: HashMap<&'a str, &'a str>) -> Builder<'a> {
self.set_variables(variables);
self
}
pub fn variable(mut self, key: &'a str, value: &'a str) -> Builder<'a> {
self.set_variable(key, value);
self
}
pub fn set_items(&mut self, items: &'a [Item]) {
self.items = items
}
pub fn set_variables(&mut self, variables: HashMap<&'a str, &'a str>) {
self.variables = variables
}
pub fn set_variable(&mut self, key: &'a str, value: &'a str) {
self.variables.insert(key, value);
}
}
impl<'a> Item<'a> {
pub fn to_json(&self) -> Value {
let mut d = json::Map::new();
d.insert("title".to_string(), json!(self.title));
if let Some(ref subtitle) = self.subtitle {
d.insert("subtitle".to_string(), json!(subtitle));
}
if let Some(ref icon) = self.icon {
d.insert("icon".to_string(), icon.to_json());
}
if let Some(ref uid) = self.uid {
d.insert("uid".to_string(), json!(uid));
}
if let Some(ref arg) = self.arg {
d.insert("arg".to_string(), json!(arg));
}
match self.type_ {
ItemType::Default => {}
ItemType::File => {
d.insert("type".to_string(), json!("file"));
}
ItemType::FileSkipCheck => {
d.insert("type".to_string(), json!("file:skipcheck"));
}
}
if !self.valid {
d.insert("valid".to_string(), Value::Bool(false));
}
if let Some(ref autocomplete) = self.autocomplete {
d.insert("autocomplete".to_string(), json!(autocomplete));
}
if self.text_copy.is_some() || self.text_large_type.is_some() {
let mut text = json::Map::new();
if let Some(ref text_copy) = self.text_copy {
text.insert("copy".to_string(), json!(text_copy));
}
if let Some(ref text_large_type) = self.text_large_type {
text.insert("largetype".to_string(), json!(text_large_type));
}
d.insert("text".to_string(), Value::Object(text));
}
if let Some(ref url) = self.quicklook_url {
d.insert("quicklookurl".to_string(), json!(url));
}
if !self.modifiers.is_empty() {
let mut mods = json::Map::with_capacity(self.modifiers.len());
for (modifier, data) in &self.modifiers {
let key = match *modifier {
Modifier::Command => "cmd",
Modifier::Option => "alt",
Modifier::Control => "ctrl",
Modifier::Shift => "shift",
Modifier::Fn => "fn"
}.to_string();
mods.insert(key, data.to_json());
}
d.insert("mods".to_string(), Value::Object(mods));
}
if !self.variables.is_empty() {
let mut vars = json::Map::with_capacity(self.variables.len());
for (key, value) in &self.variables {
vars.insert(key.clone().into_owned(), json!(value.clone().into_owned()));
}
d.insert("variables".to_string(), Value::Object(vars));
}
Value::Object(d)
}
}
impl<'a> Icon<'a> {
pub fn to_json(&self) -> Value {
match *self {
Icon::Path(ref s) => json!({"path": s}),
Icon::File(ref s) => json!({"type": "fileicon", "path": s}),
Icon::FileType(ref s) => json!({"type": "filetype", "path": s})
}
}
}
impl<'a> ModifierData<'a> {
pub fn to_json(&self) -> Value {
let mut mod_ = json::Map::new();
if let Some(ref subtitle) = self.subtitle {
mod_.insert("subtitle".to_string(), json!(subtitle));
}
if let Some(ref arg) = self.arg {
mod_.insert("arg".to_string(), json!(arg));
}
if let Some(valid) = self.valid {
mod_.insert("valid".to_string(), json!(valid));
}
if let Some(ref icon) = self.icon {
mod_.insert("icon".to_string(), icon.to_json());
}
if !self.variables.is_empty() {
let mut vars = json::Map::with_capacity(self.variables.len());
for (key, value) in &self.variables {
vars.insert(key.clone().into_owned(), json!(value.clone().into_owned()));
}
mod_.insert("variables".to_string(), Value::Object(vars));
}
Value::Object(mod_)
}
}
#[test]
fn test_to_json() {
let item = Item::new("Item 1");
assert_eq!(item.to_json(), json!({"title": "Item 1"}));
let item = ::ItemBuilder::new("Item 2")
.subtitle("Subtitle")
.into_item();
assert_eq!(item.to_json(),
json!({
"title": "Item 2",
"subtitle": "Subtitle"
}));
let item = ::ItemBuilder::new("Item 3")
.arg("Argument")
.subtitle("Subtitle")
.icon_filetype("public.folder")
.into_item();
assert_eq!(item.to_json(),
json!({
"title": "Item 3",
"subtitle": "Subtitle",
"arg": "Argument",
"icon": { "type": "filetype", "path": "public.folder" }
}));
let item = ::ItemBuilder::new("Item 4")
.arg("Argument")
.subtitle("Subtitle")
.arg_mod(Modifier::Option, "Alt Argument")
.valid_mod(Modifier::Option, false)
.icon_file_mod(Modifier::Option, "opt.png")
.arg_mod(Modifier::Control, "Ctrl Argument")
.subtitle_mod(Modifier::Control, "Ctrl Subtitle")
.icon_path_mod(Modifier::Control, "ctrl.png")
.arg_mod(Modifier::Shift, "Shift Argument")
.into_item();
assert_eq!(item.to_json(),
json!({
"title": "Item 4",
"subtitle": "Subtitle",
"arg": "Argument",
"mods": {
"alt": {
"arg": "Alt Argument",
"valid": false,
"icon": { "type": "fileicon", "path": "opt.png" }
},
"ctrl": {
"arg": "Ctrl Argument",
"subtitle": "Ctrl Subtitle",
"icon": { "path": "ctrl.png" }
},
"shift": {
"arg": "Shift Argument"
}
}
}));
let item = ::ItemBuilder::new("Item 5")
.arg("Argument")
.variable("fruit", "banana")
.variable("vegetable", "carrot")
.into_item();
assert_eq!(item.to_json(),
json!({
"title": "Item 5",
"arg": "Argument",
"variables": {
"fruit": "banana",
"vegetable": "carrot"
}
}));
let item = ::ItemBuilder::new("Item 6")
.subtitle("Subtitle")
.variable("fruit", "banana")
.variable_mod(Modifier::Option, "vegetable", "carrot")
.into_item();
assert_eq!(item.to_json(),
json!({
"title": "Item 6",
"subtitle": "Subtitle",
"mods": {
"alt": {
"variables": {
"vegetable": "carrot"
}
}
},
"variables": {
"fruit": "banana"
}
}));
}
#[test]
fn test_builder() {
let json = Builder::with_items(&[
Item::new("Item 1"),
::ItemBuilder::new("Item 2")
.subtitle("Subtitle")
.into_item(),
::ItemBuilder::new("Item 3")
.arg("Argument")
.subtitle("Subtitle")
.icon_filetype("public.folder")
.into_item()
]).variable("fruit", "banana")
.variable("vegetable", "carrot")
.into_json();
assert_eq!(json,
json!({
"items": [
{
"title": "Item 1"
},
{
"title": "Item 2",
"subtitle": "Subtitle"
},
{
"title": "Item 3",
"arg": "Argument",
"subtitle": "Subtitle",
"icon": { "type": "filetype", "path": "public.folder" }
}
],
"variables": {
"fruit": "banana",
"vegetable": "carrot"
}
}));
}