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 pub last_updated: DateTime<Utc>,
66}
67
68pub fn create_conf() -> Result<OsedaConfig, Box<dyn Error>> {
69 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}