use std::path::PathBuf;
use leetup_cache::kvstore::KvStore;
use log::debug;
use spinners::{Spinner, Spinners};
use structopt::StructOpt;
use crate::service::{CacheKey, Session};
use crate::{
service::{leetcode::Leetcode, Lang, ServiceProvider},
Config, Result,
};
#[derive(Debug, StructOpt)]
pub struct List {
pub keyword: Option<String>,
#[structopt(short, long)]
pub tag: Option<String>,
#[structopt(short, long)]
pub query: Option<String>,
#[structopt(short, long)]
pub stat: bool,
#[structopt(short, long)]
pub order: Option<String>,
}
#[derive(Debug, StructOpt)]
pub struct User {
#[structopt(short, long)]
pub cookie: Option<Option<String>>,
#[structopt(short, long)]
pub logout: Option<Option<String>>,
}
#[derive(Debug, StructOpt)]
pub struct Pick {
pub id: Option<usize>,
#[structopt(short)]
pub generate: bool,
#[structopt(short)]
pub def: bool,
#[structopt(short, long)]
pub lang: Option<Lang>,
}
#[derive(Debug, StructOpt)]
pub struct Submit {
pub filename: String,
}
#[derive(Debug, StructOpt)]
pub struct Test {
pub filename: String,
#[structopt(short)]
pub test_data: Option<Option<String>>,
}
#[derive(Debug, StructOpt)]
pub enum Command {
#[structopt(name = "list")]
List(List),
#[structopt(name = "user")]
User(User),
#[structopt(name = "pick")]
Pick(Pick),
#[structopt(name = "submit")]
Submit(Submit),
#[structopt(name = "test")]
Test(Test),
}
#[derive(Debug)]
pub enum Query {
Easy = 1,
Medium,
Hard,
NotEasy,
NotMedium,
NotHard,
Locked,
Unlocked,
Done,
NotDone,
Starred,
Unstarred,
}
impl From<char> for Query {
fn from(c: char) -> Self {
match c {
'e' => Query::Easy,
'E' => Query::NotEasy,
'm' => Query::Medium,
'M' => Query::NotMedium,
'h' => Query::Hard,
'H' => Query::NotHard,
'l' => Query::Locked,
'L' => Query::Unlocked,
'd' => Query::Done,
'D' => Query::NotDone,
's' => Query::Starred,
'S' => Query::Unstarred,
_ => Query::Easy,
}
}
}
impl Query {
pub fn from_str(q: &str) -> Vec<Query> {
q.chars().map(Query::from).collect()
}
}
pub enum OrderBy {
IdAsc,
IdDesc,
TitleAsc,
TitleDesc,
DifficultyAsc,
DifficultyDesc,
}
impl From<char> for OrderBy {
fn from(c: char) -> Self {
match c {
'i' => OrderBy::IdAsc,
'I' => OrderBy::IdDesc,
't' => OrderBy::TitleAsc,
'T' => OrderBy::TitleDesc,
'd' => OrderBy::DifficultyAsc,
'D' => OrderBy::DifficultyDesc,
_ => OrderBy::IdAsc,
}
}
}
impl OrderBy {
pub fn from_str(order: &str) -> Vec<OrderBy> {
order.chars().map(OrderBy::from).collect()
}
}
#[derive(StructOpt, Debug)]
#[structopt(name = "leetup")]
pub struct LeetUpArgs {
#[structopt(subcommand)]
pub command: Command,
}
pub async fn process() -> Result<()> {
let opt = LeetUpArgs::from_args();
debug!("Options: {:#?}", opt);
let config_dir = create_config_directory()?;
let mut cache = KvStore::open(&config_dir)?;
let session = get_session(&mut cache)?;
let config = get_config(config_dir);
debug!("Session: {:#?}", session);
debug!("Config: {:#?}", config);
let mut provider = Leetcode::new(session.as_ref(), &config, cache)?;
match opt.command {
Command::Pick(pick) => {
provider.pick_problem(pick).await?;
}
Command::List(list) => {
provider.list_problems(list).await?;
}
Command::User(user) => {
provider.process_auth(user).await?;
}
Command::Submit(submit) => {
let sp = Spinner::new(Spinners::Dots9, "Waiting for judge result!".into());
provider.problem_submit(submit).await?;
sp.stop();
}
Command::Test(test) => {
let sp = Spinner::new(Spinners::Dots9, "Waiting for judge result!".into());
provider.problem_test(test).await?;
sp.stop();
}
}
Ok(())
}
fn get_config(mut config_dir: PathBuf) -> Config {
config_dir.push("config.json");
Config::get(config_dir)
}
fn get_session(cache: &mut KvStore) -> Result<Option<Session>> {
let mut session: Option<Session> = None;
let session_val = cache.get(CacheKey::Session.into())?;
if let Some(ref val) = session_val {
session = Some(serde_json::from_str::<Session>(val)?);
}
Ok(session)
}
fn create_config_directory() -> Result<PathBuf> {
let mut data_dir = PathBuf::new();
data_dir.push(
dirs::home_dir()
.ok_or("Home directory not available!")
.map_err(anyhow::Error::msg)?,
);
data_dir.push(".leetup");
Ok(data_dir)
}