twitch_api/helix/endpoints/predictions/
create_prediction.rs1use std::marker::PhantomData;
69
70use super::*;
71use helix::RequestPost;
72
73#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)]
77#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))]
78#[must_use]
79#[non_exhaustive]
80pub struct CreatePredictionRequest<'a> {
81 #[cfg_attr(feature = "typed-builder", builder(default))]
82 #[serde(skip)]
83 _marker: PhantomData<&'a ()>,
84}
85
86impl CreatePredictionRequest<'_> {
87 pub fn new() -> Self { Self::default() }
89}
90
91#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)]
95#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))]
96#[non_exhaustive]
97pub struct CreatePredictionBody<'a> {
98 #[cfg_attr(feature = "typed-builder", builder(setter(into)))]
100 #[cfg_attr(feature = "deser_borrow", serde(borrow = "'a"))]
101 pub broadcaster_id: Cow<'a, types::UserIdRef>,
102 #[cfg_attr(feature = "typed-builder", builder(setter(into)))]
104 #[cfg_attr(feature = "deser_borrow", serde(borrow = "'a"))]
105 pub title: Cow<'a, str>,
106 #[cfg_attr(feature = "deser_borrow", serde(borrow = "'a"))]
108 pub outcomes: Cow<'a, [NewPredictionOutcome<'a>]>,
109 pub prediction_window: i64,
111}
112
113impl<'a> CreatePredictionBody<'a> {
114 pub fn new(
116 broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a,
117 title: impl Into<Cow<'a, str>>,
118 outcomes: &'a [NewPredictionOutcome<'a>],
119 prediction_window: i64,
120 ) -> Self {
121 Self {
122 broadcaster_id: broadcaster_id.into_cow(),
123 title: title.into(),
124 outcomes: outcomes.into(),
125 prediction_window,
126 }
127 }
128}
129
130impl helix::private::SealedSerialize for CreatePredictionBody<'_> {}
131
132#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)]
134#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))]
135#[non_exhaustive]
136pub struct NewPredictionOutcome<'a> {
137 #[cfg_attr(feature = "deser_borrow", serde(borrow = "'a"))]
139 pub title: Cow<'a, str>,
140}
141
142impl<'a> NewPredictionOutcome<'a> {
143 pub fn new(title: impl Into<Cow<'a, str>>) -> Self {
145 Self {
146 title: title.into(),
147 }
148 }
149}
150
151pub type CreatePredictionResponse = super::Prediction;
155
156impl Request for CreatePredictionRequest<'_> {
157 type Response = CreatePredictionResponse;
158
159 const PATH: &'static str = "predictions";
160 #[cfg(feature = "twitch_oauth2")]
161 const SCOPE: twitch_oauth2::Validator =
162 twitch_oauth2::validator![twitch_oauth2::Scope::ChannelManagePredictions];
163}
164
165impl<'a> RequestPost for CreatePredictionRequest<'a> {
166 type Body = CreatePredictionBody<'a>;
167
168 fn parse_inner_response(
169 request: Option<Self>,
170 uri: &http::Uri,
171 response_str: &str,
172 status: http::StatusCode,
173 ) -> Result<helix::Response<Self, Self::Response>, helix::HelixRequestPostError>
174 where
175 Self: Sized,
176 {
177 let response: helix::InnerResponse<Vec<Self::Response>> =
178 helix::parse_json(response_str, true).map_err(|e| {
179 helix::HelixRequestPostError::DeserializeError(
180 response_str.to_string(),
181 e,
182 uri.clone(),
183 status,
184 )
185 })?;
186 let data = response.data.into_iter().next().ok_or_else(|| {
187 helix::HelixRequestPostError::InvalidResponse {
188 reason: "response included no data",
189 response: response_str.to_string(),
190 status,
191 uri: uri.clone(),
192 }
193 })?;
194 Ok(helix::Response {
195 data,
196 pagination: response.pagination.cursor,
197 request,
198 total: None,
199 other: None,
200 })
201 }
202}
203
204#[cfg(test)]
205#[test]
206fn test_request() {
207 use helix::*;
208 let req = CreatePredictionRequest::new();
209 let outcomes = &[
210 NewPredictionOutcome::new("Yes, give it time."),
211 NewPredictionOutcome::new("Definitely not."),
212 ];
213 let body = CreatePredictionBody::new("141981764", "Any leeks in the stream?", outcomes, 120);
214
215 assert_eq!(
216 std::str::from_utf8(&body.try_to_body().unwrap()).unwrap(),
217 r#"{"broadcaster_id":"141981764","title":"Any leeks in the stream?","outcomes":[{"title":"Yes, give it time."},{"title":"Definitely not."}],"prediction_window":120}"#
218 );
219
220 dbg!(req.create_request(body, "token", "clientid").unwrap());
221
222 let data = br##"
224{
225 "data": [
226 {
227 "id": "bc637af0-7766-4525-9308-4112f4cbf178",
228 "broadcaster_id": "141981764",
229 "broadcaster_name": "TwitchDev",
230 "broadcaster_login": "twitchdev",
231 "title": "Any leeks in the stream?",
232 "winning_outcome_id": null,
233 "outcomes": [
234 {
235 "id": "73085848-a94d-4040-9d21-2cb7a89374b7",
236 "title": "Yes, give it time.",
237 "users": 0,
238 "channel_points": 0,
239 "top_predictors": null,
240 "color": "BLUE"
241 },
242 {
243 "id": "906b70ba-1f12-47ea-9e95-e5f93d20e9cc",
244 "title": "Definitely not.",
245 "users": 0,
246 "channel_points": 0,
247 "top_predictors": null,
248 "color": "PINK"
249 }
250 ],
251 "prediction_window": 120,
252 "status": "ACTIVE",
253 "created_at": "2021-04-28T17:11:22.595914172Z",
254 "ended_at": null,
255 "locked_at": null
256 }
257 ]
258}
259 "##
260 .to_vec();
261
262 let http_response = http::Response::builder().status(200).body(data).unwrap();
263 let uri = req.get_uri().unwrap();
266 assert_eq!(uri.to_string(), "https://api.twitch.tv/helix/predictions?");
267
268 dbg!(CreatePredictionRequest::parse_response(Some(req), &uri, http_response).unwrap());
269}