Better error handling
This commit is contained in:
		
							parent
							
								
									227e29e9c4
								
							
						
					
					
						commit
						8b967001f4
					
				| 
						 | 
				
			
			@ -70,25 +70,32 @@ async fn main() {
 | 
			
		|||
    let json = matches.get_one::<bool>("json").unwrap();
 | 
			
		||||
    let settings = Settings { project: project.to_string(), url: url.to_string(), json: *json };
 | 
			
		||||
 | 
			
		||||
    match matches.subcommand() {
 | 
			
		||||
    let result = match matches.subcommand() {
 | 
			
		||||
        Some(("start", sub_matches)) => {
 | 
			
		||||
            let description = sub_matches.get_one::<String>("DESCRIPTION");
 | 
			
		||||
            start(settings, description).await.unwrap();
 | 
			
		||||
            start(settings, description).await
 | 
			
		||||
        }
 | 
			
		||||
        Some(("stop", sub_matches)) => {
 | 
			
		||||
            let id = sub_matches.get_one::<i32>("ID");
 | 
			
		||||
            stop(settings, id).await.unwrap();
 | 
			
		||||
            stop(settings, id).await
 | 
			
		||||
        }
 | 
			
		||||
        Some(("edit", sub_matches)) => {
 | 
			
		||||
            let since = sub_matches.get_one::<String>("since");
 | 
			
		||||
            let until = sub_matches.get_one::<String>("until");
 | 
			
		||||
            let num = sub_matches.get_one::<String>("NUM");
 | 
			
		||||
            edit(settings, since, until, num).await.unwrap();
 | 
			
		||||
            edit(settings, since, until, num).await
 | 
			
		||||
        }
 | 
			
		||||
        Some(("status", _)) => {
 | 
			
		||||
            status(settings).await.unwrap();
 | 
			
		||||
            status(settings).await
 | 
			
		||||
        }
 | 
			
		||||
        _ => cli().print_help().unwrap(),
 | 
			
		||||
    }
 | 
			
		||||
        _ => {
 | 
			
		||||
            cli().print_help().unwrap();
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if let Err(e) = result {
 | 
			
		||||
        eprintln!("{}", e);
 | 
			
		||||
        std::process::exit(1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ use std::{
 | 
			
		|||
    process::Command,
 | 
			
		||||
};
 | 
			
		||||
use colored::*;
 | 
			
		||||
use crate::error::CliError;
 | 
			
		||||
 | 
			
		||||
pub struct Settings {
 | 
			
		||||
    pub url: String,
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +28,7 @@ pub struct WorkPeriod {
 | 
			
		|||
    pub description: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn start(settings: Settings, description: Option<&String>) -> Result<(), reqwest::Error> {
 | 
			
		||||
pub async fn start(settings: Settings, description: Option<&String>) -> Result<(), CliError> {
 | 
			
		||||
    let mut map = HashMap::new();
 | 
			
		||||
    map.insert("project", settings.project);
 | 
			
		||||
    if let Some(description) = description {
 | 
			
		||||
| 
						 | 
				
			
			@ -40,12 +41,12 @@ pub async fn start(settings: Settings, description: Option<&String>) -> Result<(
 | 
			
		|||
        .await?;
 | 
			
		||||
 | 
			
		||||
    if !response.status().is_success() {
 | 
			
		||||
        println!("{:?}", response.text().await);
 | 
			
		||||
        return Err(CliError::Server(response.text().await?));
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn stop(settings: Settings, id: Option<&i32>) -> Result<(), reqwest::Error> {
 | 
			
		||||
pub async fn stop(settings: Settings, id: Option<&i32>) -> Result<(), CliError> {
 | 
			
		||||
    let mut map = HashMap::new();
 | 
			
		||||
    map.insert("project", settings.project);
 | 
			
		||||
    if let Some(id) = id {
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +60,7 @@ pub async fn stop(settings: Settings, id: Option<&i32>) -> Result<(), reqwest::E
 | 
			
		|||
        .await?;
 | 
			
		||||
 | 
			
		||||
    if !response.status().is_success() {
 | 
			
		||||
        println!("{:?}", response.text().await);
 | 
			
		||||
        return Err(CliError::Server(response.text().await?));
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +116,7 @@ fn edit_periods(periods: Vec<WorkPeriod>) -> Result<Vec<WorkPeriod>, std::io::Er
 | 
			
		|||
    Ok(periods)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn update_period(settings: &Settings, period: WorkPeriod) -> Result<(), reqwest::Error> {
 | 
			
		||||
pub async fn update_period(settings: &Settings, period: WorkPeriod) -> Result<(), CliError> {
 | 
			
		||||
    let client = Client::new();
 | 
			
		||||
    let response = client.put(settings.url.to_string() + "/api/history/" + &period.id.unwrap().to_string())
 | 
			
		||||
        .json(&period)
 | 
			
		||||
| 
						 | 
				
			
			@ -123,12 +124,12 @@ pub async fn update_period(settings: &Settings, period: WorkPeriod) -> Result<()
 | 
			
		|||
        .await?;
 | 
			
		||||
 | 
			
		||||
    if !response.status().is_success() {
 | 
			
		||||
        println!("{:?}", response.text().await);
 | 
			
		||||
        return Err(CliError::Server(response.text().await?));
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn add_period(settings: &Settings, period: &mut WorkPeriod) -> Result<(), reqwest::Error> {
 | 
			
		||||
pub async fn add_period(settings: &Settings, period: &mut WorkPeriod) -> Result<(), CliError> {
 | 
			
		||||
    let client = Client::new();
 | 
			
		||||
    period.id = None;
 | 
			
		||||
    let response = client.post(settings.url.to_string() + "/api/history")
 | 
			
		||||
| 
						 | 
				
			
			@ -137,24 +138,24 @@ pub async fn add_period(settings: &Settings, period: &mut WorkPeriod) -> Result<
 | 
			
		|||
        .await?;
 | 
			
		||||
 | 
			
		||||
    if !response.status().is_success() {
 | 
			
		||||
        println!("{:?}", response.text().await);
 | 
			
		||||
        return Err(CliError::Server(response.text().await?));
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn delete_period(settings: &Settings, id: i32) -> Result<(), reqwest::Error> {
 | 
			
		||||
pub async fn delete_period(settings: &Settings, id: i32) -> Result<(), CliError> {
 | 
			
		||||
    let client = Client::new();
 | 
			
		||||
    let response = client.delete(settings.url.to_string() + "/api/history/" + id.to_string().as_str())
 | 
			
		||||
        .send()
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    if !response.status().is_success() {
 | 
			
		||||
        println!("{:?}", response.text().await);
 | 
			
		||||
        return Err(CliError::Server(response.text().await?));
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn edit(settings: Settings, since: Option<&String>, until: Option<&String>, num: Option<&String>) -> Result<(), reqwest::Error> {
 | 
			
		||||
pub async fn edit(settings: Settings, since: Option<&String>, until: Option<&String>, num: Option<&String>) -> Result<(), CliError> {
 | 
			
		||||
    let mut params = vec![
 | 
			
		||||
        ("project", settings.project.to_owned()),
 | 
			
		||||
    ];
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +176,7 @@ pub async fn edit(settings: Settings, since: Option<&String>, until: Option<&Str
 | 
			
		|||
        .await?;
 | 
			
		||||
 | 
			
		||||
    if !response.status().is_success() {
 | 
			
		||||
        println!("{:?}", response.text().await);
 | 
			
		||||
        return Err(CliError::Server(response.text().await?));
 | 
			
		||||
    } else {
 | 
			
		||||
        let body = response.text().await.unwrap();
 | 
			
		||||
        let periods = parse_periods(body).unwrap();
 | 
			
		||||
| 
						 | 
				
			
			@ -234,13 +235,19 @@ fn format_bar(seconds: i64, max_value: i64, width: usize) -> String {
 | 
			
		|||
    format!("│{}", colored_bar)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn status(settings: Settings) -> Result<(), reqwest::Error> {
 | 
			
		||||
pub async fn status(settings: Settings) -> Result<(), CliError> {
 | 
			
		||||
    // Get additional metrics from status endpoint
 | 
			
		||||
    let client = Client::new();
 | 
			
		||||
    let status_response = client.get(settings.url.to_string() + "/")
 | 
			
		||||
        .timeout(std::time::Duration::from_secs(10))
 | 
			
		||||
        .send()
 | 
			
		||||
        .await?;
 | 
			
		||||
    let status: Status = serde_json::from_str(&status_response.text().await?).unwrap();
 | 
			
		||||
 | 
			
		||||
    if !status_response.status().is_success() {
 | 
			
		||||
        return Err(CliError::Server(status_response.text().await?));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let status: Status = serde_json::from_str(&status_response.text().await?)?;
 | 
			
		||||
 | 
			
		||||
    // Compute current week boundaries (Monday to Sunday)
 | 
			
		||||
    let now = chrono::Local::now();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
use std::fmt;
 | 
			
		||||
use colored::*;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum CliError {
 | 
			
		||||
    Network(String),
 | 
			
		||||
    Server(String),
 | 
			
		||||
    Parse(String),
 | 
			
		||||
    Other(String),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::error::Error for CliError {}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for CliError {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        let (label, message) = match self {
 | 
			
		||||
            CliError::Network(msg) => ("Network Error".red(), msg),
 | 
			
		||||
            CliError::Server(msg) => ("Server Error".red(), msg),
 | 
			
		||||
            CliError::Parse(msg) => ("Parse Error".red(), msg),
 | 
			
		||||
            CliError::Other(msg) => ("Error".red(), msg),
 | 
			
		||||
        };
 | 
			
		||||
        write!(f, "{}: {}", label, message)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<reqwest::Error> for CliError {
 | 
			
		||||
    fn from(err: reqwest::Error) -> Self {
 | 
			
		||||
        if err.is_connect() {
 | 
			
		||||
            CliError::Network("Could not connect to server".to_string())
 | 
			
		||||
        } else if err.is_timeout() {
 | 
			
		||||
            CliError::Network("Connection timed out".to_string())
 | 
			
		||||
        } else if err.is_status() {
 | 
			
		||||
            CliError::Server(format!("Server returned error: {}", err.status().unwrap_or_default()))
 | 
			
		||||
        } else {
 | 
			
		||||
            CliError::Network(err.to_string())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<serde_json::Error> for CliError {
 | 
			
		||||
    fn from(err: serde_json::Error) -> Self {
 | 
			
		||||
        CliError::Parse(format!("Failed to parse server response: {}", err))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1 +1,2 @@
 | 
			
		|||
pub mod commands;
 | 
			
		||||
pub mod error;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue