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 pub last_updated: DateTime<Utc>,
61}
62
63pub fn create_conf() -> Result<OsedaConfig, Box<dyn Error>> {
64 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}