use derive_builder::Builder;
use crate::api::common::CommaSeparatedList;
use crate::api::endpoint_prelude::*;
use crate::api::ParamValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum RunnerAccessLevel {
NotProtected,
RefProtected,
}
impl RunnerAccessLevel {
fn as_str(self) -> &'static str {
match self {
Self::NotProtected => "not_protected",
Self::RefProtected => "ref_protected",
}
}
}
impl ParamValue<'static> for RunnerAccessLevel {
fn as_value(&self) -> Cow<'static, str> {
self.as_str().into()
}
}
#[derive(Debug, Builder, Clone)]
#[builder(setter(strip_option), build_fn(validate = "Self::validate"))]
pub struct EditRunner<'a> {
runner: u64,
#[builder(setter(into), default)]
description: Option<Cow<'a, str>>,
#[builder(default)]
paused: Option<bool>,
#[builder(setter(name = "_tag_list"), default, private)]
tag_list: Option<CommaSeparatedList<Cow<'a, str>>>,
#[builder(default)]
run_untagged: Option<bool>,
#[builder(default)]
locked: Option<bool>,
#[builder(default)]
access_level: Option<RunnerAccessLevel>,
#[builder(default)]
maximum_timeout: Option<u64>,
#[builder(setter(into), default)]
maintenance_note: Option<Cow<'a, str>>,
}
impl<'a> EditRunner<'a> {
pub fn builder() -> EditRunnerBuilder<'a> {
EditRunnerBuilder::default()
}
}
impl<'a> EditRunnerBuilder<'a> {
pub fn tag<T>(&mut self, tag: T) -> &mut Self
where
T: Into<Cow<'a, str>>,
{
self.tag_list
.get_or_insert(None)
.get_or_insert_with(CommaSeparatedList::new)
.push(tag.into());
self
}
pub fn tags<I, T>(&mut self, iter: I) -> &mut Self
where
I: Iterator<Item = T>,
T: Into<Cow<'a, str>>,
{
self.tag_list
.get_or_insert(None)
.get_or_insert_with(CommaSeparatedList::new)
.extend(iter.map(|t| t.into()));
self
}
fn validate(&self) -> Result<(), EditRunnerBuilderError> {
if let Some(Some(maintenance_note)) = self.maintenance_note.as_ref() {
if maintenance_note.len() > super::MAX_MAINTENANCE_NOTE_LENGTH {
return Err(format!(
"`maintenance_note` may be at most {} bytes",
super::MAX_MAINTENANCE_NOTE_LENGTH,
)
.into());
}
}
Ok(())
}
}
impl Endpoint for EditRunner<'_> {
fn method(&self) -> Method {
Method::PUT
}
fn endpoint(&self) -> Cow<'static, str> {
format!("runners/{}", self.runner).into()
}
fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
let mut params = FormParams::default();
params
.push_opt("description", self.description.as_ref())
.push_opt("paused", self.paused)
.push_opt("tag_list", self.tag_list.as_ref())
.push_opt("run_untagged", self.run_untagged)
.push_opt("locked", self.locked)
.push_opt("access_level", self.access_level)
.push_opt("maximum_timeout", self.maximum_timeout)
.push_opt("maintenance_note", self.maintenance_note.as_ref());
params.into_body()
}
}
#[cfg(test)]
mod tests {
use http::Method;
use crate::api::runners::{EditRunner, EditRunnerBuilderError, RunnerAccessLevel};
use crate::api::{self, Query};
use crate::test::client::{ExpectedUrl, SingleTestClient};
#[test]
fn runner_access_level_as_str() {
let items = &[
(RunnerAccessLevel::NotProtected, "not_protected"),
(RunnerAccessLevel::RefProtected, "ref_protected"),
];
for (i, s) in items {
assert_eq!(i.as_str(), *s);
}
}
#[test]
fn runner_is_required() {
let err = EditRunner::builder().build().unwrap_err();
crate::test::assert_missing_field!(err, EditRunnerBuilderError, "runner");
}
#[test]
fn runner_is_sufficient() {
EditRunner::builder().runner(1).build().unwrap();
}
#[test]
fn maintenance_note_length() {
use crate::api::runners::MAX_MAINTENANCE_NOTE_LENGTH;
let too_long = format!("{:width$}", "note", width = MAX_MAINTENANCE_NOTE_LENGTH + 1);
let err = EditRunner::builder()
.runner(1)
.maintenance_note(too_long)
.build()
.unwrap_err();
if let EditRunnerBuilderError::ValidationError(message) = err {
assert_eq!(
message,
format!("`maintenance_note` may be at most {MAX_MAINTENANCE_NOTE_LENGTH} bytes"),
);
} else {
panic!("unexpected error: {:?}", err);
}
}
#[test]
fn endpoint() {
let endpoint = ExpectedUrl::builder()
.method(Method::PUT)
.endpoint("runners/1")
.content_type("application/x-www-form-urlencoded")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = EditRunner::builder().runner(1).build().unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_description() {
let endpoint = ExpectedUrl::builder()
.method(Method::PUT)
.endpoint("runners/1")
.content_type("application/x-www-form-urlencoded")
.body_str("description=desc")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = EditRunner::builder()
.runner(1)
.description("desc")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_paused() {
let endpoint = ExpectedUrl::builder()
.method(Method::PUT)
.endpoint("runners/1")
.content_type("application/x-www-form-urlencoded")
.body_str("paused=true")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = EditRunner::builder()
.runner(1)
.paused(true)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_tag_list() {
let endpoint = ExpectedUrl::builder()
.method(Method::PUT)
.endpoint("runners/1")
.content_type("application/x-www-form-urlencoded")
.body_str("tag_list=tag2%2Ctag1%2Ctag3")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = EditRunner::builder()
.runner(1)
.tag("tag2")
.tags(["tag1", "tag3"].iter().cloned())
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_run_untagged() {
let endpoint = ExpectedUrl::builder()
.method(Method::PUT)
.endpoint("runners/1")
.content_type("application/x-www-form-urlencoded")
.body_str("run_untagged=false")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = EditRunner::builder()
.runner(1)
.run_untagged(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_locked() {
let endpoint = ExpectedUrl::builder()
.method(Method::PUT)
.endpoint("runners/1")
.content_type("application/x-www-form-urlencoded")
.body_str("locked=false")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = EditRunner::builder()
.runner(1)
.locked(false)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_access_level() {
let endpoint = ExpectedUrl::builder()
.method(Method::PUT)
.endpoint("runners/1")
.content_type("application/x-www-form-urlencoded")
.body_str("access_level=ref_protected")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = EditRunner::builder()
.runner(1)
.access_level(RunnerAccessLevel::RefProtected)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_maximum_timeout() {
let endpoint = ExpectedUrl::builder()
.method(Method::PUT)
.endpoint("runners/1")
.content_type("application/x-www-form-urlencoded")
.body_str("maximum_timeout=3600")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = EditRunner::builder()
.runner(1)
.maximum_timeout(3600)
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
#[test]
fn endpoint_maintenance_note() {
let endpoint = ExpectedUrl::builder()
.method(Method::PUT)
.endpoint("runners/1")
.content_type("application/x-www-form-urlencoded")
.body_str("maintenance_note=note")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");
let endpoint = EditRunner::builder()
.runner(1)
.maintenance_note("note")
.build()
.unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
}