oseda_cli/
config.rs

1use std::error::Error;
2use std::fs::File;
3use std::io::{self, BufWriter, Write};
4use std::{ffi::OsString, fs};
5
6use chrono::{DateTime, Utc};
7use inquire::validator::Validation;
8use reqwest::blocking::get;
9use serde::{Deserialize, Serialize};
10use strum::IntoEnumIterator;
11
12use crate::categories::Category;
13use crate::cmd::check::OsedaCheckError;
14use crate::github;
15
16pub fn read_and_validate_config() -> Result<OsedaConfig, OsedaCheckError> {
17    let config_str = fs::read_to_string("oseda-config.json").map_err(|_| {
18        OsedaCheckError::MissingConfig(format!(
19            "Could not find config file in {}",
20            std::env::current_dir().unwrap().to_str().unwrap()
21        ))
22    })?;
23
24    let conf: OsedaConfig = serde_json::from_str(&config_str)
25        .map_err(|_| OsedaCheckError::BadConfig("Could not parse oseda config file".to_owned()))?;
26
27    let gh_name = github::get_config("user.name").ok_or_else(|| {
28        OsedaCheckError::BadGitCredentials("Could not get git user.name from git config".to_owned())
29    })?;
30
31    if gh_name != conf.author {
32        return Err(OsedaCheckError::BadGitCredentials(
33            "Config author does not match git credentials".to_owned(),
34        ));
35    }
36
37    let path = std::env::current_dir().map_err(|_| {
38        OsedaCheckError::DirectoryNameMismatch("Could not get path of working directory".to_owned())
39    })?;
40
41    let cwd = path.file_name().ok_or_else(|| {
42        OsedaCheckError::DirectoryNameMismatch("Could not resolve path name".to_owned())
43    })?;
44
45    if cwd != OsString::from(conf.title.clone()) {
46        return Err(OsedaCheckError::DirectoryNameMismatch(
47            "Config title does not match directory name".to_owned(),
48        ));
49    }
50
51    Ok(conf)
52}
53
54#[derive(Serialize, Deserialize)]
55pub struct OsedaConfig {
56    pub title: String,
57    pub author: String,
58    pub category: Vec<Category>,
59    // effectively mutable. Will get updated on each deployment
60    pub last_updated: DateTime<Utc>,
61}
62
63pub fn create_conf() -> Result<OsedaConfig, Box<dyn Error>> {
64    // let mut title = String::new();
65    // std::io::stdin().read_line(&mut title)?;
66
67    let validator = |input: &str| {
68        if input.chars().count() < 2 {
69            Ok(Validation::Invalid(
70                ("Title must be longer than two characters").into(),
71            ))
72        } else {
73            Ok(Validation::Valid)
74        }
75    };
76
77    let mut title = inquire::Text::new("Title: ")
78        .with_validator(validator)
79        .prompt()?;
80
81    title = title.replace(" ", "-");
82
83    let categories = get_categories()?;
84
85    let user_name = github::get_config("user.name")
86        .ok_or_else(|| "Could not get github username. Please ensure you are signed into github")?;
87
88    Ok(OsedaConfig {
89        title: title.trim().to_owned(),
90        author: user_name,
91        category: categories,
92        last_updated: get_time(),
93    })
94}
95
96fn get_categories() -> Result<Vec<Category>, Box<dyn Error>> {
97    let options: Vec<Category> = Category::iter().collect();
98
99    let selected_categories =
100        inquire::MultiSelect::new("Select categories", options.clone()).prompt()?;
101
102    println!("You selected:");
103    for category in selected_categories.iter().copied() {
104        println!("- {:?}", category);
105    }
106
107    return Ok(selected_categories);
108}
109
110pub fn update_time(mut conf: OsedaConfig) -> Result<(), Box<dyn Error>> {
111    conf.last_updated = get_time();
112
113    write_config(".", &conf)?;
114    Ok(())
115}
116
117fn get_time() -> DateTime<Utc> {
118    chrono::offset::Utc::now()
119}
120
121pub fn write_config(path: &str, conf: &OsedaConfig) -> Result<(), Box<dyn Error>> {
122    let file = File::create(format!("{}/oseda-config.json", path))?;
123    let writer = BufWriter::new(file);
124
125    serde_json::to_writer_pretty(writer, &conf)?;
126
127    Ok(())
128}