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