1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! Service layer for [`ApplicationSetting`].

use crate::error::ServiceError;
use crate::model::dto::ApplicationSettingDto;
use crate::model::entity::ApplicationSetting;
use diesel_async::AsyncPgConnection;
use reqwest::StatusCode;

/// RGB value of one color
type Rgb = (u8, u8, u8);

/// RGB values of all four heatmap colors (red, orange, green, black)
pub type HeatmapColors = (Rgb, Rgb, Rgb, Rgb);

/// Find an application setting by key
///
/// # Errors
/// If the connection to the database could not be established.
/// If no application setting with the key `key` exists.
pub async fn find_by_key(
    key: &str,
    conn: &mut AsyncPgConnection,
) -> Result<ApplicationSettingDto, ServiceError> {
    let result = ApplicationSetting::find_by_key(key, conn).await?;
    Ok(result)
}

/// Convert a hex color code to RGB.
/// Hex code must be 6 characters long (case-insensitive).
///
/// # Errors
/// If the hex code is not 6 characters long.
/// If the hex code is not valid.
pub fn hex_to_rgb(hex: &str) -> Result<(u8, u8, u8), ServiceError> {
    let error_msg = "Invalid heatmap color hex code";
    if hex.len() != 6 {
        return Err(ServiceError::new(
            StatusCode::INTERNAL_SERVER_ERROR,
            error_msg,
        ));
    }

    let red_slice = hex.get(0..2);
    let red = match red_slice {
        Some(red) => u8::from_str_radix(red, 16)
            .map_err(|_| ServiceError::new(StatusCode::INTERNAL_SERVER_ERROR, error_msg))?,
        None => {
            return Err(ServiceError::new(
                StatusCode::INTERNAL_SERVER_ERROR,
                error_msg,
            ));
        }
    };

    let green_slice = hex.get(2..4);
    let green = match green_slice {
        Some(green) => u8::from_str_radix(green, 16)
            .map_err(|_| ServiceError::new(StatusCode::INTERNAL_SERVER_ERROR, error_msg))?,
        None => {
            return Err(ServiceError::new(
                StatusCode::INTERNAL_SERVER_ERROR,
                error_msg,
            ));
        }
    };

    let blue_slice = hex.get(4..6);
    let blue = match blue_slice {
        Some(blue) => u8::from_str_radix(blue, 16)
            .map_err(|_| ServiceError::new(StatusCode::INTERNAL_SERVER_ERROR, error_msg))?,
        None => {
            return Err(ServiceError::new(
                StatusCode::INTERNAL_SERVER_ERROR,
                error_msg,
            ));
        }
    };

    Ok((red, green, blue))
}

/// Get all four heatmap colors (red, orange, green, black) from the application settings.
///
/// # Errors
/// If the connection to the database could not be established.
/// If the colors in the database are not valid hex codes.
/// If there are no colors in the database.
pub async fn get_heatmap_colors(
    conn: &mut AsyncPgConnection,
) -> Result<HeatmapColors, ServiceError> {
    let red_hex = find_by_key("heatmap_red", conn).await?;
    let orange_hex = find_by_key("heatmap_orange", conn).await?;
    let green_hex = find_by_key("heatmap_green", conn).await?;
    let black_hex = find_by_key("heatmap_black", conn).await?;

    let red = hex_to_rgb(&red_hex.value).map_err(ServiceError::from)?;
    let orange = hex_to_rgb(&orange_hex.value).map_err(ServiceError::from)?;
    let green = hex_to_rgb(&green_hex.value).map_err(ServiceError::from)?;
    let black = hex_to_rgb(&black_hex.value).map_err(ServiceError::from)?;

    Ok((red, orange, green, black))
}