summaryrefslogtreecommitdiff
path: root/utils/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'utils/src/main.rs')
-rw-r--r--utils/src/main.rs103
1 files changed, 103 insertions, 0 deletions
diff --git a/utils/src/main.rs b/utils/src/main.rs
new file mode 100644
index 0000000..d49a3d7
--- /dev/null
+++ b/utils/src/main.rs
@@ -0,0 +1,103 @@
+use serde::{Deserialize, Serialize};
+use serde_dhall::StaticType;
+use serde_json as json;
+
+use std::fs;
+use std::io::Write;
+
+use std::path::PathBuf;
+use structopt::StructOpt;
+
+use age::x25519::Recipient;
+
+#[derive(Deserialize, Serialize, StaticType, Debug)]
+struct Survey {
+ title: String,
+ description: String,
+ questions: Vec<Question>,
+ pubkey: Option<String>
+}
+
+#[derive(Deserialize, Serialize, StaticType, Debug)]
+struct Question {
+ question: String,
+ name: String,
+ space: AnswerSpace,
+}
+
+#[derive(Deserialize, Serialize, StaticType, Debug)]
+enum AnswerSpace {
+ Single(Vec<String>),
+ Multiple(Vec<String>),
+ YesOrNo,
+ Freeform(String),
+}
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Options {
+ /// a dhall configuration file that describes a survey
+ #[structopt(long, short, parse(from_os_str))]
+ config_file: PathBuf,
+ /// encrypt the survey with the given password (not yet implemented)
+ #[structopt(long, short)]
+ password: Option<secrecy::Secret<String>>,
+}
+
+fn main () {
+ let opt = Options::from_args();
+
+ let config_file = std::fs::read_to_string(opt.config_file).unwrap();
+
+ // hacky way to get a "prelude" in dhall which doesn't have to be
+ // imported: just wrap our input code into a dhall "let"-statement.
+ // Probably doesn't scale very vell, though ...
+ let code = format!(
+ "let Question = {} \nlet Answers = {} \nin {}",
+ Question::static_type(),
+ AnswerSpace::static_type(),
+ config_file
+ );
+ match serde_dhall::from_str(&code)
+ .static_type_annotation()
+ .parse::<Survey>()
+ {
+ Err(e) => {
+ eprintln!("There is an error in your dhall code!\n{}", e);
+ std::process::exit(1);
+ },
+ Ok(data) => {
+ let json = json::to_string(&data).unwrap();
+
+ // if a public key is given to encrypt the survey, ad-hoc typecheck it
+ // (not sure if the dhall crate allows defining custom types which are
+ // opaque to dhall ...)
+ match data.pubkey {
+ Some (key) => key.parse::<Recipient>().is_err(),
+ None => false
+ }.then(|| {
+ println!("field pubkey is not a valid public key, aborting ...");
+ std::process::exit(1);
+ });
+
+
+ // out here to avoid borrowing issues — if it were in the password
+ // branch below, it would go out of scope at its end, since .as_slice()
+ // just borrows its argument
+ let mut encrypted = vec![];
+ // are we restricting access to the survey? if so, encrypt it with
+ // the password as passphrase.
+ let outdata = match opt.password {
+ None => json.as_bytes(),
+ Some(password) => {
+ let encryptor = age::Encryptor::with_user_passphrase(password);
+ let mut writer = encryptor.wrap_output(&mut encrypted).unwrap();
+ writer.write_all(&json.as_bytes()).unwrap();
+ writer.finish().unwrap();
+ encrypted.as_slice()
+ }
+ };
+ fs::write("outfile", outdata).expect("cannot write to outfile!");
+ }
+ }
+}