1use std::error::Error;
2use std::fs::File;
3use std::io::BufWriter;
4use std::{ffi::OsString, fs};
5
6use chrono::{DateTime, Utc};
7use inquire::validator::Validation;
8use serde::{Deserialize, Serialize};
9use strum::IntoEnumIterator;
10
11use crate::categories::Category;
12use crate::cmd::check::OsedaCheckError;
13use crate::github;
14
15pub fn read_and_validate_config() -> Result<OsedaConfig, OsedaCheckError> {
29 let config_str = fs::read_to_string("oseda-config.json").map_err(|_| {
30 OsedaCheckError::MissingConfig(format!(
31 "Could not find config file in {}",
32 std::env::current_dir().unwrap().to_str().unwrap()
33 ))
34 })?;
35
36 let conf: OsedaConfig = serde_json::from_str(&config_str)
37 .map_err(|_| OsedaCheckError::BadConfig("Could not parse oseda config file".to_owned()))?;
38
39 let is_in_ci = std::env::var("GITHUB_ACTIONS").map_or(false, |v| v == "true");
41 let skip_git = is_in_ci;
42
43 if !skip_git {
44 println!("Running git checks");
45 let gh_name = github::get_config("user.name").ok_or_else(|| {
46 OsedaCheckError::BadGitCredentials(
47 "Could not get git user.name from git config".to_owned(),
48 )
49 })?;
50
51 if gh_name != conf.author {
52 return Err(OsedaCheckError::BadGitCredentials(
53 "Config author does not match git credentials".to_owned(),
54 ));
55 }
56 }
57
58 let path = std::env::current_dir().map_err(|_| {
59 OsedaCheckError::DirectoryNameMismatch("Could not get path of working directory".to_owned())
60 })?;
61
62 let cwd = path.file_name().ok_or_else(|| {
63 OsedaCheckError::DirectoryNameMismatch("Could not resolve path name".to_owned())
64 })?;
65
66 if cwd != OsString::from(conf.title.clone()) {
67 return Err(OsedaCheckError::DirectoryNameMismatch(
68 "Config title does not match directory name".to_owned(),
69 ));
70 }
71
72 Ok(conf)
73}
74
75#[derive(Serialize, Deserialize)]
77pub struct OsedaConfig {
78 pub title: String,
79 pub author: String,
80 pub category: Vec<Category>,
81 pub last_updated: DateTime<Utc>,
83}
84
85pub fn create_conf() -> Result<OsedaConfig, Box<dyn Error>> {
91 let validator = |input: &str| {
95 if input.chars().count() < 2 {
96 Ok(Validation::Invalid(
97 ("Title must be longer than two characters").into(),
98 ))
99 } else {
100 Ok(Validation::Valid)
101 }
102 };
103
104 let mut title = inquire::Text::new("Title: ")
105 .with_validator(validator)
106 .prompt()?;
107
108 title = title.replace(" ", "-");
109
110 let categories = get_categories()?;
111
112 let user_name = github::get_config("user.name")
113 .ok_or("Could not get github username. Please ensure you are signed into github")?;
114
115 Ok(OsedaConfig {
116 title: title.trim().to_owned(),
117 author: user_name,
118 category: categories,
119 last_updated: get_time(),
120 })
121}
122
123fn get_categories() -> Result<Vec<Category>, Box<dyn Error>> {
129 let options: Vec<Category> = Category::iter().collect();
130
131 let selected_categories =
132 inquire::MultiSelect::new("Select categories", options.clone()).prompt()?;
133
134 println!("You selected:");
135 for category in selected_categories.iter() {
136 println!("- {:?}", category);
137 }
138
139 Ok(selected_categories)
140}
141
142pub fn update_time(mut conf: OsedaConfig) -> Result<(), Box<dyn Error>> {
152 conf.last_updated = get_time();
153
154 write_config(".", &conf)?;
155 Ok(())
156}
157
158fn get_time() -> DateTime<Utc> {
163 chrono::offset::Utc::now()
164}
165
166pub fn write_config(path: &str, conf: &OsedaConfig) -> Result<(), Box<dyn Error>> {
176 let file = File::create(format!("{}/oseda-config.json", path))?;
177 let writer = BufWriter::new(file);
178
179 serde_json::to_writer_pretty(writer, &conf)?;
180
181 Ok(())
182}