use std::collections::HashMap;
use super::*;
use helix::RequestPut;
#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)]
#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))]
#[non_exhaustive]
pub struct UpdateUserExtensionsRequest<'a> {
#[serde(skip)]
_phantom: std::marker::PhantomData<&'a ()>,
}
impl UpdateUserExtensionsRequest<'_> {
pub fn new() -> Self { Self::default() }
}
impl Request for UpdateUserExtensionsRequest<'_> {
type Response = ExtensionConfiguration;
const PATH: &'static str = "users/extensions";
#[cfg(feature = "twitch_oauth2")]
const SCOPE: twitch_oauth2::Validator =
twitch_oauth2::validator![twitch_oauth2::Scope::UserEditBroadcast];
}
#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)]
#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))]
#[non_exhaustive]
pub struct UpdateUserExtensionsBody<'a> {
pub data: ExtensionSpecification<'a>,
}
impl helix::private::SealedSerialize for UpdateUserExtensionsBody<'_> {}
impl<'a> UpdateUserExtensionsBody<'a> {
pub const fn new(data: ExtensionSpecification<'a>) -> Self { Self { data } }
}
#[derive(PartialEq, Eq, Deserialize, Serialize, Debug, Clone, Default)]
#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))]
#[non_exhaustive]
pub struct ExtensionSpecification<'a> {
pub panel: Option<HashMap<Cow<'a, str>, ExtensionSlot<ActiveExtension<'a>>>>,
pub overlay: Option<HashMap<Cow<'a, str>, ExtensionSlot<ActiveExtension<'a>>>>,
pub component: Option<HashMap<Cow<'a, str>, ExtensionSlot<ActivePositionedExtension<'a>>>>,
}
impl<'a> ExtensionSpecification<'a> {
pub fn new() -> Self { Self::default() }
pub fn panel(
mut self,
panel: HashMap<Cow<'a, str>, ExtensionSlot<ActiveExtension<'a>>>,
) -> Self {
self.panel = Some(panel);
self
}
pub fn overlay(
mut self,
overlay: HashMap<Cow<'a, str>, ExtensionSlot<ActiveExtension<'a>>>,
) -> Self {
self.overlay = Some(overlay);
self
}
pub fn component(
mut self,
component: HashMap<Cow<'a, str>, ExtensionSlot<ActivePositionedExtension<'a>>>,
) -> Self {
self.component = Some(component);
self
}
}
#[derive(PartialEq, Eq, Deserialize, Serialize, Debug, Clone)]
#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))]
#[non_exhaustive]
pub struct ActiveExtension<'a> {
pub id: Cow<'a, types::ExtensionIdRef>,
pub version: Cow<'a, str>,
}
impl<'a> ActiveExtension<'a> {
pub fn new(
id: impl types::IntoCow<'a, types::ExtensionIdRef> + 'a,
version: impl Into<Cow<'a, str>>,
) -> Self {
Self {
id: id.into_cow(),
version: version.into(),
}
}
}
#[derive(PartialEq, Eq, Deserialize, Serialize, Debug, Clone)]
#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))]
#[non_exhaustive]
pub struct ActivePositionedExtension<'a> {
pub id: Cow<'a, types::ExtensionIdRef>,
pub version: Cow<'a, str>,
pub x: i32,
pub y: i32,
}
impl<'a> ActivePositionedExtension<'a> {
pub fn new(
id: impl types::IntoCow<'a, types::ExtensionIdRef> + 'a,
version: impl Into<Cow<'a, str>>,
x: i32,
y: i32,
) -> Self {
Self {
id: id.into_cow(),
version: version.into(),
x,
y,
}
}
}
impl<'a> RequestPut for UpdateUserExtensionsRequest<'a> {
type Body = UpdateUserExtensionsBody<'a>;
fn parse_inner_response(
request: Option<Self>,
uri: &http::Uri,
response: &str,
status: http::StatusCode,
) -> Result<helix::Response<Self, <Self as Request>::Response>, helix::HelixRequestPutError>
where
Self: Sized,
{
let inner_response: helix::InnerResponse<<Self as Request>::Response> =
crate::parse_json(response, true).map_err(|e| {
helix::HelixRequestPutError::DeserializeError(
response.to_string(),
e,
uri.clone(),
status,
)
})?;
Ok(helix::Response::new(
inner_response.data,
inner_response.pagination.cursor,
request,
inner_response.total,
inner_response.other,
))
}
}
#[cfg(test)]
#[test]
fn test_request() {
use helix::*;
use std::iter::FromIterator;
let req = UpdateUserExtensionsRequest::new();
let spec = ExtensionSpecification::new()
.panel(HashMap::from_iter([
(
Cow::Borrowed("1"),
ExtensionSlot::Active(ActiveExtension::new(
"rh6jq1q334hqc2rr1qlzqbvwlfl3x0",
"1.1.0",
)),
),
(
Cow::Borrowed("2"),
ExtensionSlot::Active(ActiveExtension::new(
"wi08ebtatdc7oj83wtl9uxwz807l8b",
"1.1.8",
)),
),
(
Cow::Borrowed("3"),
ExtensionSlot::Active(ActiveExtension::new(
"naty2zwfp7vecaivuve8ef1hohh6bo",
"1.0.9",
)),
),
]))
.overlay(HashMap::from_iter([(
Cow::Borrowed("1"),
ExtensionSlot::Active(ActiveExtension::new(
"zfh2irvx2jb4s60f02jq0ajm8vwgka",
"1.0.19",
)),
)]))
.component(HashMap::from_iter([
(
Cow::Borrowed("1"),
ExtensionSlot::Active(ActivePositionedExtension::new(
"lqnf3zxk0rv0g7gq92mtmnirjz2cjj",
"0.0.1",
0,
0,
)),
),
(Cow::Borrowed("2"), ExtensionSlot::Inactive),
]));
let body = UpdateUserExtensionsBody::new(spec);
assert_eq!(
body,
serde_json::from_str(&serde_json::to_string(&body).unwrap()).unwrap()
);
dbg!(req.create_request(body, "token", "clientid").unwrap());
let data = br#"
{
"data": {
"panel": {
"1": {
"active": true,
"id": "rh6jq1q334hqc2rr1qlzqbvwlfl3x0",
"version": "1.1.0",
"name": "TopClip"
},
"2": {
"active": true,
"id": "wi08ebtatdc7oj83wtl9uxwz807l8b",
"version": "1.1.8",
"name": "Streamlabs Leaderboard"
},
"3": {
"active": true,
"id": "naty2zwfp7vecaivuve8ef1hohh6bo",
"version": "1.0.9",
"name": "Streamlabs Stream Schedule & Countdown"
}
},
"overlay": {
"1": {
"active": true,
"id": "zfh2irvx2jb4s60f02jq0ajm8vwgka",
"version": "1.0.19",
"name": "Streamlabs"
}
},
"component": {
"1": {
"active": true,
"id": "lqnf3zxk0rv0g7gq92mtmnirjz2cjj",
"version": "0.0.1",
"name": "Dev Experience Test",
"x": 0,
"y": 0
},
"2": {
"active": false
}
}
}
}
"#
.to_vec();
let http_response = http::Response::builder().body(data).unwrap();
let uri = req.get_uri().unwrap();
assert_eq!(
uri.to_string(),
"https://api.twitch.tv/helix/users/extensions?"
);
let res = UpdateUserExtensionsRequest::parse_response(Some(req), &uri, http_response)
.unwrap()
.data;
assert_eq!(res.panel.len(), 3);
assert_eq!(res.overlay.len(), 1);
assert_eq!(res.component.len(), 2);
assert_eq!(*res.component.get("2").unwrap(), ExtensionSlot::Inactive);
}