use derive_builder::Builder;
use crate::api::common::NameOrId;
use crate::api::endpoint_prelude::*;
use crate::api::ParamValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LabelPriority {
Value(u64),
Null,
}
impl LabelPriority {
fn as_str(self) -> Cow<'static, str> {
match self {
LabelPriority::Null => "null".into(),
LabelPriority::Value(priority) => format!("{priority}").into(),
}
}
}
impl From<u64> for LabelPriority {
fn from(prio: u64) -> Self {
Self::Value(prio)
}
}
impl ParamValue<'static> for LabelPriority {
fn as_value(&self) -> Cow<'static, str> {
self.as_str()
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum GitlabDefaultColor {
MagentaPink,
Crimson,
RoseRed,
DarkCoral,
CarrotOrange,
TitaniumYellow,
GreenCyan,
DarkSeaGreen,
BlueGray,
Lavender,
DarkViolet,
DeepViolet,
CharcoalGrey,
Gray,
}
impl GitlabDefaultColor {
fn to_label_color(self) -> LabelColor {
match self {
Self::MagentaPink => LabelColor::rgb(0xcc, 0x33, 0x8b),
Self::Crimson => LabelColor::rgb(0xdc, 0x14, 0x3c),
Self::RoseRed => LabelColor::rgb(0xc2, 0x1e, 0x56),
Self::DarkCoral => LabelColor::rgb(0xcd, 0x5b, 0x45),
Self::CarrotOrange => LabelColor::rgb(0xed, 0x91, 0x21),
Self::TitaniumYellow => LabelColor::rgb(0xee, 0xe6, 0x00),
Self::GreenCyan => LabelColor::rgb(0x00, 0x99, 0x66),
Self::DarkSeaGreen => LabelColor::rgb(0x8f, 0xbc, 0x8f),
Self::BlueGray => LabelColor::rgb(0x66, 0x99, 0xcc),
Self::Lavender => LabelColor::rgb(0xe6, 0xe6, 0xfa),
Self::DarkViolet => LabelColor::rgb(0x94, 0x00, 0xd3),
Self::DeepViolet => LabelColor::rgb(0x33, 0x00, 0x66),
Self::CharcoalGrey => LabelColor::rgb(0x36, 0x45, 0x4f),
Self::Gray => LabelColor::rgb(0x80, 0x80, 0x80),
}
}
}
#[derive(Debug, Clone)]
enum RgbOrString {
Rgb(u8, u8, u8),
String(String),
}
#[derive(Debug, Clone)]
pub struct LabelColor {
color: RgbOrString,
}
impl LabelColor {
fn rgb(red: u8, green: u8, blue: u8) -> Self {
let color = RgbOrString::Rgb(red, green, blue);
Self {
color,
}
}
fn named<N>(name: N) -> Self
where
N: Into<String>,
{
let color = RgbOrString::String(name.into());
Self {
color,
}
}
fn as_str(&self) -> String {
match &self.color {
RgbOrString::Rgb(r, g, b) => format!("#{r:02x}{g:02x}{b:02x}"),
RgbOrString::String(string) => string.clone(),
}
}
}
impl From<GitlabDefaultColor> for LabelColor {
fn from(gdc: GitlabDefaultColor) -> Self {
gdc.to_label_color()
}
}
impl From<(u8, u8, u8)> for LabelColor {
fn from((r, g, b): (u8, u8, u8)) -> Self {
Self::rgb(r, g, b)
}
}
impl From<&str> for LabelColor {
fn from(s: &str) -> Self {
Self::named(s)
}
}
impl From<String> for LabelColor {
fn from(s: String) -> Self {
Self::named(s)
}
}
impl From<Cow<'_, str>> for LabelColor {
fn from(s: Cow<str>) -> Self {
Self::named(s)
}
}
impl ParamValue<'static> for &LabelColor {
fn as_value(&self) -> Cow<'static, str> {
self.as_str().into()
}
}
#[derive(Debug, Builder, Clone)]
#[builder(setter(strip_option))]
pub struct CreateLabel<'a> {
#[builder(setter(into))]
project: NameOrId<'a>,
#[builder(setter(into))]
name: Cow<'a, str>,
#[builder(setter(into))]
color: LabelColor,
#[builder(setter(into), default)]
description: Option<Cow<'a, str>>,
#[builder(setter(into), default)]
priority: Option<LabelPriority>,
}
impl<'a> CreateLabel<'a> {
pub fn builder() -> CreateLabelBuilder<'a> {
CreateLabelBuilder::default()
}
}
impl Endpoint for CreateLabel<'_> {
fn method(&self) -> Method {
Method::POST
}
fn endpoint(&self) -> Cow<'static, str> {
format!("projects/{}/labels", self.project).into()
}
fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
let mut params = FormParams::default();
params
.push("name", &self.name)
.push("color", &self.color)
.push_opt("description", self.description.as_ref())
.push_opt("priority", self.priority);
params.into_body()
}
}
#[cfg(test)]
mod tests {
use http::Method;
use crate::api::projects::labels::{
CreateLabel, CreateLabelBuilderError, GitlabDefaultColor, LabelColor, LabelPriority,
};
use crate::api::{self, Query};
use crate::test::client::{ExpectedUrl, SingleTestClient};
#[test]
fn label_priority_as_str() {
let items = &[
(LabelPriority::Null, "null"),
(LabelPriority::Value(100), "100"),
];
for (i, s) in items {
assert_eq!(i.as_str(), *s);
}
}
#[test]
fn label_color_as_str() {
let items = &[
(GitlabDefaultColor::MagentaPink.into(), "#cc338b"),
(GitlabDefaultColor::Crimson.into(), "#dc143c"),
(GitlabDefaultColor::RoseRed.into(), "#c21e56"),
(GitlabDefaultColor::DarkCoral.into(), "#cd5b45"),
(GitlabDefaultColor::CarrotOrange.into(), "#ed9121"),
(GitlabDefaultColor::TitaniumYellow.into(), "#eee600"),
(GitlabDefaultColor::GreenCyan.into(), "#009966"),
(GitlabDefaultColor::DarkSeaGreen.into(), "#8fbc8f"),
(GitlabDefaultColor::BlueGray.into(), "#6699cc"),
(GitlabDefaultColor::Lavender.into(), "#e6e6fa"),
(GitlabDefaultColor::DarkViolet.into(), "#9400d3"),
(GitlabDefaultColor::DeepViolet.into(), "#330066"),
(GitlabDefaultColor::CharcoalGrey.into(), "#36454f"),
(GitlabDefaultColor::Gray.into(), "#808080"),
(LabelColor::rgb(0xf1, 0x00, 0xfe), "#f100fe"),
(LabelColor::named("blue"), "blue"),
];
for (i, s) in items {
assert_eq!(i.as_str(), *s);
}
}
#[test]
fn project_name_and_color_are_necessary() {
let err = CreateLabel::builder().build().unwrap_err();
crate::test::assert_missing_field!(err, CreateLabelBuilderError, "project");
}
#[test]
fn project_is_necessary() {
let err = CreateLabel::builder()
.name("label")
.color((0xf1, 0x00, 0xfe))
.build()
.unwrap_err();
crate::test::assert_missing_field!(err, CreateLabelBuilderError, "project");
}
#[test]
fn name_is_necessary() {
let err = CreateLabel::builder()
.project(1)
.color((0xf1, 0x00, 0xfe))
.build()
.unwrap_err();
crate::test::assert_missing_field!(err, CreateLabelBuilderError, "name");
}
#[test]
fn color_is_necessary() {
let err = CreateLabel::builder()
.project(1)
.name("label")
.build()
.unwrap_err();
crate::test::assert_missing_field!(err, CreateLabelBuilderError, "color");
}
#[test]
fn project_name_and_color_are_sufficient() {
CreateLabel::builder()
.project(1)
.name("label")
.color((0xf1, 0x00, 0xfe))
.build()
.unwrap();
}
#[test]
fn endpoint() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/labels")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("name=label", "&color=%23ffffff"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateLabel::builder()
.project("simple/project")
.name("label")
.color((0xff, 0xff, 0xff))
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_description() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/labels")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!(
"name=label",
"&color=%23ffffff",
"&description=description",
))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateLabel::builder()
.project("simple/project")
.name("label")
.color((0xff, 0xff, 0xff))
.description("description")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_priority() {
let endpoint = ExpectedUrl::builder()
.method(Method::POST)
.endpoint("projects/simple%2Fproject/labels")
.content_type("application/x-www-form-urlencoded")
.body_str(concat!("name=label", "&color=%23808080", "&priority=1"))
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = CreateLabel::builder()
.project("simple/project")
.name("label")
.color(GitlabDefaultColor::Gray)
.priority(1)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
}