git_bug/entities/issue/data/
label.rs

1// git-bug-rs - A rust library for interfacing with git-bug repositories
2//
3// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
4// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
7// This file is part of git-bug-rs/git-gub.
8//
9// You should have received a copy of the License along with this program.
10// If not, see <https://www.gnu.org/licenses/agpl.txt>.
11
12//! Concrete type of an [`Issue's`][`super::super::Issue`] Label.
13
14use std::fmt::Display;
15
16use serde::{Deserialize, Serialize};
17use sha2::{Digest, Sha256};
18use simd_json::{borrowed, derived::ValueTryIntoString, owned};
19
20/// A label for an issue.
21///
22/// This can be an arbitrary string, but should never contain a newline.
23#[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize, Serialize)]
24pub struct Label(pub(crate) String);
25
26impl From<&str> for Label {
27    fn from(value: &str) -> Self {
28        Self(value.to_owned())
29    }
30}
31
32impl TryFrom<owned::Value> for Label {
33    type Error = value_parse::Error;
34
35    fn try_from(value: owned::Value) -> Result<Self, Self::Error> {
36        let string = value
37            .try_into_string()
38            .map_err(|err| value_parse::Error::StrExpected { err })?;
39
40        Ok(Self(string))
41    }
42}
43
44impl<'a> From<&'a Label> for borrowed::Value<'a> {
45    fn from(val: &'a Label) -> Self {
46        borrowed::Value::String(std::borrow::Cow::Borrowed(&val.0))
47    }
48}
49
50#[allow(missing_docs)]
51pub mod value_parse {
52    #[derive(Debug, thiserror::Error)]
53    pub enum Error {
54        #[error("Expected string in json data, but found something else: {err}")]
55        StrExpected { err: simd_json::TryTypeError },
56    }
57}
58
59impl Display for Label {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        self.0.fmt(f)
62    }
63}
64
65impl Label {
66    /// RGBA from a Label computed in a deterministic way
67    /// This is taken completely from `git-bug`
68    #[must_use]
69    pub fn associate_color(&self) -> Color {
70        // colors from: https://material-ui.com/style/color/
71        let colors = [
72            Color::from_rgba(244, 67, 54, 255),   // red
73            Color::from_rgba(233, 30, 99, 255),   // pink
74            Color::from_rgba(156, 39, 176, 255),  // purple
75            Color::from_rgba(103, 58, 183, 255),  // deepPurple
76            Color::from_rgba(63, 81, 181, 255),   // indigo
77            Color::from_rgba(33, 150, 243, 255),  // blue
78            Color::from_rgba(3, 169, 244, 255),   // lightBlue
79            Color::from_rgba(0, 188, 212, 255),   // cyan
80            Color::from_rgba(0, 150, 136, 255),   // teal
81            Color::from_rgba(76, 175, 80, 255),   // green
82            Color::from_rgba(139, 195, 74, 255),  // lightGreen
83            Color::from_rgba(205, 220, 57, 255),  // lime
84            Color::from_rgba(255, 235, 59, 255),  // yellow
85            Color::from_rgba(255, 193, 7, 255),   // amber
86            Color::from_rgba(255, 152, 0, 255),   // orange
87            Color::from_rgba(255, 87, 34, 255),   // deepOrange
88            Color::from_rgba(121, 85, 72, 255),   // brown
89            Color::from_rgba(158, 158, 158, 255), // grey
90            Color::from_rgba(96, 125, 139, 255),  // blueGrey
91        ];
92
93        let hash = Sha256::digest(self.to_string().as_bytes());
94
95        let id: usize = hash
96            .into_iter()
97            .map(|val| val as usize)
98            .fold(0, |acc, val| (acc + val) % colors.len());
99
100        colors[id]
101    }
102}
103
104#[derive(Default, Clone, Copy, Debug)]
105/// The possible Color of an Lable.
106///
107/// This encodes the rgba values of the color in the common format (0 -> 0%;
108/// 255/[`u8::MAX`] -> 100%).
109pub struct Color {
110    /// The amount of red.
111    pub red: u8,
112
113    /// The amount of green.
114    pub green: u8,
115
116    /// The amount of blue.
117    pub blue: u8,
118
119    /// How transparent this color is.
120    pub alpha: u8,
121}
122
123impl Color {
124    /// Construct an Color from it's components.
125    #[must_use]
126    pub fn from_rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
127        Self {
128            red,
129            green,
130            blue,
131            alpha,
132        }
133    }
134}