use crate::blocks::RichText;
use crate::blocks::elements::UrlSource;
use crate::validators::*;
use serde::Serialize;
use slack_messaging_derive::Builder;
#[derive(Debug, Clone, Serialize, PartialEq, Builder)]
#[serde(tag = "type", rename = "task_card")]
pub struct TaskCard {
#[builder(validate("required"))]
pub(crate) task_id: Option<String>,
#[builder(validate("required"))]
pub(crate) title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(validate("rich_text::single_element"))]
pub(crate) details: Option<RichText>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(validate("rich_text::single_element"))]
pub(crate) output: Option<RichText>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(push_item = "source")]
pub(crate) sources: Option<Vec<UrlSource>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) status: Option<TaskStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(validate("text::max_255"))]
pub(crate) block_id: Option<String>,
}
#[derive(Debug, Copy, Clone, Serialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum TaskStatus {
Pending,
InProgress,
Complete,
Error,
}
#[cfg(test)]
mod tests {
use super::super::rich_text::{
RichText, test_helpers::section, types::RichTextElementType, types::test_helpers::el_text,
};
use super::*;
use crate::blocks::rich_text::RichTextSubElement;
use crate::errors::*;
#[test]
fn it_implements_builder() {
let output = rich_text(vec![el_text("output of task card")]);
let details = rich_text(vec![el_text("details for task card")]);
let sources = vec![
url_source("https://weather.com/", "weather.com"),
url_source("https://www.accuweather.com/", "accuweather.com"),
];
let expected = TaskCard {
task_id: Some("task_1".into()),
title: Some("Fetching weather data".into()),
status: Some(TaskStatus::Pending),
output: Some(output.clone()),
details: Some(details.clone()),
sources: Some(sources.clone()),
block_id: Some("task_card_0".into()),
};
let val = TaskCard::builder()
.set_task_id(Some("task_1"))
.set_title(Some("Fetching weather data"))
.set_status(Some(TaskStatus::Pending))
.set_output(Some(output.clone()))
.set_details(Some(details.clone()))
.set_sources(Some(sources.clone()))
.set_block_id(Some("task_card_0"))
.build()
.unwrap();
assert_eq!(val, expected);
let val = TaskCard::builder()
.task_id("task_1")
.title("Fetching weather data")
.status(TaskStatus::Pending)
.output(output.clone())
.details(details.clone())
.sources(sources.clone())
.block_id("task_card_0")
.build()
.unwrap();
assert_eq!(val, expected);
}
#[test]
fn it_implements_push_item_method() {
let expected = TaskCard {
task_id: Some("task_1".into()),
title: Some("Fetching weather data".into()),
status: None,
output: None,
details: None,
sources: Some(vec![
url_source("https://weather.com/", "weather.com"),
url_source("https://www.accuweather.com/", "accuweather.com"),
]),
block_id: None,
};
let val = TaskCard::builder()
.task_id("task_1")
.title("Fetching weather data")
.source(url_source("https://weather.com/", "weather.com"))
.source(url_source(
"https://www.accuweather.com/",
"accuweather.com",
))
.build()
.unwrap();
assert_eq!(val, expected);
}
#[test]
fn it_requires_task_id_field() {
let err = TaskCard::builder().title("foo").build().unwrap_err();
assert_eq!(err.object(), "TaskCard");
let errors = err.field("task_id");
assert!(errors.includes(ValidationErrorKind::Required));
}
#[test]
fn it_requires_title_field() {
let err = TaskCard::builder().task_id("task_1").build().unwrap_err();
assert_eq!(err.object(), "TaskCard");
let errors = err.field("title");
assert!(errors.includes(ValidationErrorKind::Required));
}
#[test]
fn it_requires_details_and_output_fields_to_have_exactly_one_element() {
let err = TaskCard::builder()
.task_id("task_1")
.title("foo")
.details(rich_text(vec![]))
.build()
.unwrap_err();
assert_eq!(err.object(), "TaskCard");
let errors = err.field("details");
assert!(errors.includes(ValidationErrorKind::RichTextSingleElement));
let err = TaskCard::builder()
.task_id("task_1")
.title("foo")
.output(rich_text(vec![]))
.build()
.unwrap_err();
assert_eq!(err.object(), "TaskCard");
let errors = err.field("output");
assert!(errors.includes(ValidationErrorKind::RichTextSingleElement));
}
#[test]
fn it_requires_block_id_less_than_255_characters_long() {
let err = TaskCard::builder()
.task_id("task_1")
.title("foo")
.block_id("a".repeat(256))
.build()
.unwrap_err();
assert_eq!(err.object(), "TaskCard");
let errors = err.field("block_id");
assert!(errors.includes(ValidationErrorKind::MaxTextLength(255)));
}
fn rich_text(texts: Vec<RichTextElementType>) -> RichText {
RichText {
block_id: None,
elements: Some(
texts
.into_iter()
.map(|text| section(vec![text]))
.map(RichTextSubElement::from)
.collect(),
),
}
}
fn url_source(url: &str, text: &str) -> UrlSource {
UrlSource {
url: Some(url.into()),
text: Some(text.into()),
}
}
}