use clap::{self, App, SubCommand, Arg, AppSettings};
use std::time::Duration;
use std::path::PathBuf;
use std::env::home_dir;
use std::str::FromStr;
use std::fs;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum Subsystem {
Init {
force: bool,
},
AddUser {
verbose: bool,
},
QueueTweet {
file_to_load: Option<PathBuf>,
},
StartDaemon {
delay: Duration,
verbose: bool,
},
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Options {
pub config_dir: (String, PathBuf),
pub subsystem: Subsystem,
}
impl Options {
pub fn parse() -> Options {
let matches = App::new("tweetr")
.version(crate_version!())
.author(crate_authors!())
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::VersionlessSubcommands)
.setting(AppSettings::SubcommandRequiredElseHelp)
.about("tweetr is a platform that allows you to create and queue tweets to be shared when YOU want.\n\
You create content when you have time and then use FOSS and NOT pay whatever-ridiculous\n\
amount of $$$ for posting them automatically")
.arg(Arg::from_usage("-c --config-dir=[CONFIG_DIR] 'Directory containing configuration. Default: $HOME/.tweetr'")
.validator(Options::config_dir_validator))
.subcommand(SubCommand::with_name("init")
.about("Initialise global app data")
.arg(Arg::from_usage("-f --force 'Override current app configuration'")))
.subcommand(SubCommand::with_name("add-user")
.about("Add and authorise a user")
.arg(Arg::from_usage("-v --verbose 'Print more user data'")))
.subcommand(SubCommand::with_name("queue-tweet")
.about("Add a tweet to the queue")
.arg(Arg::from_usage("-f --file=[file] 'Load tweets from the specified file'").validator(Options::tweets_file_validator)))
.subcommand(SubCommand::with_name("start-daemon")
.about("Start the tweet-posting daemon")
.args(&[Arg::from_usage("-v --verbose 'Log all network requests'"),
Arg::from_usage("--delay=<delay> 'How long to wait between trying to post again [ms]'")
.default_value("60000")
.validator(Options::duration_validator)]))
.get_matches();
Options {
config_dir: match matches.value_of("config-dir") {
Some(dirs) => (dirs.to_string(), fs::canonicalize(dirs).unwrap()),
None => {
match home_dir() {
Some(mut hd) => {
hd = hd.canonicalize().unwrap();
hd.push(".tweetr");
fs::create_dir_all(&hd).unwrap();
("$HOME/.tweetr".to_string(), hd)
}
None => {
clap::Error {
message: "Couldn't automatically get home directory, please specify configuration directory with the -c option".to_string(),
kind: clap::ErrorKind::MissingRequiredArgument,
info: None,
}
.exit()
}
}
}
},
subsystem: match matches.subcommand() {
("init", Some(init_matches)) => Subsystem::Init { force: init_matches.is_present("force") },
("add-user", Some(add_user_matches)) => Subsystem::AddUser { verbose: add_user_matches.is_present("verbose") },
("queue-tweet", Some(queue_tweet_matches)) => {
Subsystem::QueueTweet { file_to_load: queue_tweet_matches.value_of("file").map(fs::canonicalize).map(Result::unwrap) }
}
("start-daemon", Some(start_daemon_matches)) => {
Subsystem::StartDaemon {
delay: Duration::from_millis(u64::from_str(start_daemon_matches.value_of("delay").unwrap()).unwrap()),
verbose: start_daemon_matches.is_present("verbose"),
}
}
_ => panic!("No subcommand passed"),
},
}
}
fn config_dir_validator(s: String) -> Result<(), String> {
fs::canonicalize(&s).map(|_| ()).map_err(|_| format!("Configuration directory \"{}\" not found", s))
}
fn tweets_file_validator(s: String) -> Result<(), String> {
fs::canonicalize(&s).map(|_| ()).map_err(|_| format!("File with tweets \"{}\" not found", s))
}
fn duration_validator(s: String) -> Result<(), String> {
u64::from_str(&s).map(|_| ()).map_err(|_| format!("\"{}\" is not a valid amount of milliseconds", s))
}
}