Skip to content

Commit 5f38674

Browse files
author
Heiko Alexander Weber
authored
Issue/#5 (#6)
1 parent 4f5652b commit 5f38674

4 files changed

Lines changed: 105 additions & 119 deletions

File tree

src/check_features.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#[cfg(not(feature = "backend+cli"))]
2+
#[cfg(not(feature = "backend+ui"))]
3+
extern crate DO_NOT_COMPILE_WITHOUT_ANY_ENABLED_BACKEND;

src/config/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub struct Config {
88
#[serde(rename_all = "snake_case")]
99
pub struct Template {
1010
pub content: Content,
11-
pub values: std::collections::BTreeMap<String, ValueDefinition>,
11+
pub values: std::collections::BTreeMap<String, VariableDefinition>,
1212
}
1313

1414
#[derive(Debug, serde::Serialize, serde::Deserialize)]
@@ -20,7 +20,7 @@ pub enum Content {
2020

2121
#[derive(Debug, serde::Serialize, serde::Deserialize)]
2222
#[serde(rename_all = "snake_case")]
23-
pub enum ValueDefinition {
23+
pub enum VariableDefinition {
2424
Static(String),
2525
Prompt(String),
2626
Shell(String),

src/main.rs

Lines changed: 7 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,12 @@
1-
#[cfg(not(feature = "backend+cli"))]
2-
#[cfg(not(feature = "backend+ui"))]
3-
extern crate DO_NOT_COMPILE_WITHOUT_ANY_ENABLED_BACKEND;
4-
5-
extern crate clap;
6-
#[cfg(feature = "backend+ui")]
7-
extern crate cursive;
8-
#[cfg(feature = "backend+cli")]
9-
extern crate dialoguer;
10-
#[cfg(feature = "backend+ui")]
11-
extern crate fui;
12-
extern crate handlebars;
13-
extern crate serde;
14-
extern crate serde_json;
15-
extern crate serde_yaml;
1+
include!("check_features.rs");
162

173
use futures::executor::block_on;
18-
use std::collections::BTreeMap;
4+
195
use std::io::{Result, Write};
206

217
pub mod args;
22-
use args::{Backend, ShellTrust};
238
pub mod config;
24-
use config::{Config, Content, Template};
25-
pub mod print;
26-
use print::Print;
27-
28-
#[allow(clippy::needless_lifetimes)]
29-
async fn select_template<'a>(config: &'a Config, backend: &Backend) -> Result<&'a Template> {
30-
let keys: Vec<String> = config.templates.keys().map(|t| t.to_owned()).collect();
31-
32-
let be = backend.to_input()?;
33-
let selection = be.select("", keys.as_slice()).await?;
34-
35-
match config.templates.get(&selection) {
36-
Some(x) => Ok(x),
37-
None => Err(std::io::Error::new(std::io::ErrorKind::Other, "failed")),
38-
}
39-
}
40-
41-
async fn get_values(
42-
template: &Template,
43-
shell_trust: &ShellTrust,
44-
backend: &Backend,
45-
) -> Result<BTreeMap<String, String>> {
46-
let mut values = BTreeMap::new();
47-
for value in &template.values {
48-
values.insert(
49-
value.0.to_owned(),
50-
value.1.execute(shell_trust, backend).await?,
51-
);
52-
}
53-
Ok(values)
54-
}
55-
56-
async fn replace(template: &str, values: &BTreeMap<String, String>) -> Result<String> {
57-
fn recursive_add(
58-
namespace: &mut std::collections::VecDeque<String>,
59-
parent: &mut serde_json::Value,
60-
value: &str,
61-
) {
62-
let current_namespace = namespace.pop_front().unwrap();
63-
match namespace.len() {
64-
0 => {
65-
parent
66-
.as_object_mut()
67-
.unwrap()
68-
.entry(&current_namespace)
69-
.or_insert(serde_json::Value::String(value.to_owned()));
70-
}
71-
_ => {
72-
let p = parent
73-
.as_object_mut()
74-
.unwrap()
75-
.entry(&current_namespace)
76-
.or_insert(serde_json::Value::Object(serde_json::Map::new()));
77-
recursive_add(namespace, p, value);
78-
}
79-
}
80-
}
81-
82-
let mut hb = handlebars::Handlebars::new();
83-
hb.set_strict_mode(true);
84-
85-
let mut values_json = serde_json::Value::Object(serde_json::Map::new());
86-
for val in values {
87-
let namespaces_vec: Vec<String> = val.0.split('.').map(|s| s.to_string()).collect();
88-
let mut namespaces = std::collections::VecDeque::from(namespaces_vec);
89-
recursive_add(&mut namespaces, &mut values_json, val.1);
90-
}
91-
92-
let rendered_template = hb.render_template(template, &values_json).unwrap();
93-
Ok(rendered_template)
94-
}
95-
96-
async fn print(invoke_options: args::PrintArguments) -> Result<()> {
97-
let cfg: Config = serde_yaml::from_str(&invoke_options.configuration).unwrap();
98-
99-
let template = select_template(&cfg, &invoke_options.backend).await?;
100-
let template_str = match &template.content {
101-
Content::Inline(x) => x.to_owned(),
102-
Content::File(x) => std::fs::read_to_string(x)?,
103-
};
104-
let values = get_values(
105-
&template,
106-
&invoke_options.shell_trust,
107-
&invoke_options.backend,
108-
)
109-
.await?;
110-
let rendered = replace(&template_str, &values).await?;
111-
112-
std::io::stdout().write_all(rendered.as_bytes())?;
113-
Ok(())
114-
}
9+
pub mod render;
11510

11611
async fn default_config() -> String {
11712
r###"version: 0.6
@@ -165,7 +60,10 @@ async fn async_main() -> Result<()> {
16560
std::fs::write("./.complate/config.yml", default_config().await)?;
16661
Ok(())
16762
}
168-
crate::args::Command::Print(x) => print(x).await,
63+
crate::args::Command::Print(x) => {
64+
std::io::stdout().write_all(crate::render::select_and_render(x).await?.as_bytes())?;
65+
Ok(())
66+
}
16967
}
17068
}
17169

src/print/mod.rs renamed to src/render/mod.rs

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,93 @@
11
use crate::args::{Backend, ShellTrust};
2-
use crate::config::ValueDefinition;
2+
use crate::config::{Config, Content, Template, VariableDefinition};
33
use async_trait::async_trait;
4+
use std::collections::BTreeMap;
45
use std::io::{Error, ErrorKind, Result};
56

7+
pub async fn render(template: &str, values: &BTreeMap<String, String>) -> Result<String> {
8+
fn recursive_add(
9+
namespace: &mut std::collections::VecDeque<String>,
10+
parent: &mut serde_json::Value,
11+
value: &str,
12+
) {
13+
let current_namespace = namespace.pop_front().unwrap();
14+
match namespace.len() {
15+
0 => {
16+
parent
17+
.as_object_mut()
18+
.unwrap()
19+
.entry(&current_namespace)
20+
.or_insert(serde_json::Value::String(value.to_owned()));
21+
}
22+
_ => {
23+
let p = parent
24+
.as_object_mut()
25+
.unwrap()
26+
.entry(&current_namespace)
27+
.or_insert(serde_json::Value::Object(serde_json::Map::new()));
28+
recursive_add(namespace, p, value);
29+
}
30+
}
31+
}
32+
33+
let mut hb = handlebars::Handlebars::new();
34+
hb.set_strict_mode(true);
35+
36+
let mut values_json = serde_json::Value::Object(serde_json::Map::new());
37+
for val in values {
38+
let namespaces_vec: Vec<String> = val.0.split('.').map(|s| s.to_string()).collect();
39+
let mut namespaces = std::collections::VecDeque::from(namespaces_vec);
40+
recursive_add(&mut namespaces, &mut values_json, val.1);
41+
}
42+
43+
let rendered_template = hb.render_template(template, &values_json).unwrap();
44+
Ok(rendered_template)
45+
}
46+
47+
#[allow(clippy::needless_lifetimes)]
48+
pub async fn select_template<'a>(config: &'a Config, backend: &Backend) -> Result<&'a Template> {
49+
let keys: Vec<String> = config.templates.keys().map(|t| t.to_owned()).collect();
50+
51+
let be = backend.to_input()?;
52+
let selection = be.select("", keys.as_slice()).await?;
53+
54+
match config.templates.get(&selection) {
55+
Some(x) => Ok(x),
56+
None => Err(std::io::Error::new(std::io::ErrorKind::Other, "failed")),
57+
}
58+
}
59+
60+
pub async fn populate_variables(
61+
vars: &std::collections::BTreeMap<String, VariableDefinition>,
62+
shell_trust: &ShellTrust,
63+
backend: &Backend,
64+
) -> Result<BTreeMap<String, String>> {
65+
let mut values = BTreeMap::new();
66+
for var in vars {
67+
values.insert(var.0.to_owned(), var.1.execute(shell_trust, backend).await?);
68+
}
69+
Ok(values)
70+
}
71+
72+
pub async fn select_and_render(invoke_options: crate::args::PrintArguments) -> Result<String> {
73+
let cfg: Config = serde_yaml::from_str(&invoke_options.configuration).unwrap();
74+
75+
let template = select_template(&cfg, &invoke_options.backend).await?;
76+
let template_str = match &template.content {
77+
Content::Inline(x) => x.to_owned(),
78+
Content::File(x) => std::fs::read_to_string(x)?,
79+
};
80+
let values = populate_variables(
81+
&template.values,
82+
&invoke_options.shell_trust,
83+
&invoke_options.backend,
84+
)
85+
.await?;
86+
render(&template_str, &values).await
87+
}
88+
689
#[async_trait]
7-
pub trait Print {
90+
pub trait Resolve {
891
async fn execute(&self, shell_trust: &ShellTrust, backend: &Backend) -> Result<String>;
992
}
1093

@@ -28,16 +111,18 @@ impl Backend {
28111
}
29112

30113
#[async_trait]
31-
impl Print for ValueDefinition {
114+
impl Resolve for VariableDefinition {
32115
async fn execute(&self, shell_trust: &ShellTrust, backend: &Backend) -> Result<String> {
33116
let backend_impl = backend.to_input()?;
34117

35118
match self {
36-
ValueDefinition::Static(v) => Ok(v.to_owned()),
37-
ValueDefinition::Prompt(v) => backend_impl.prompt(v).await,
38-
ValueDefinition::Shell(cmd) => backend_impl.shell(cmd, shell_trust).await,
39-
ValueDefinition::Select { text, options } => backend_impl.select(text, options).await,
40-
ValueDefinition::Check { text, options } => backend_impl.check(text, options).await,
119+
VariableDefinition::Static(v) => Ok(v.to_owned()),
120+
VariableDefinition::Prompt(v) => backend_impl.prompt(v).await,
121+
VariableDefinition::Shell(cmd) => backend_impl.shell(cmd, shell_trust).await,
122+
VariableDefinition::Select { text, options } => {
123+
backend_impl.select(text, options).await
124+
}
125+
VariableDefinition::Check { text, options } => backend_impl.check(text, options).await,
41126
}
42127
}
43128
}

0 commit comments

Comments
 (0)