mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-09 09:22:18 +02:00
@@ -68,9 +68,12 @@ async fn m3u_api_get(
|
||||
}
|
||||
|
||||
async fn m3u_api_post(
|
||||
axum::extract::Query(api_query_req): axum::extract::Query<UserApiRequest>,
|
||||
axum::extract::State(app_state): axum::extract::State<Arc<AppState>>,
|
||||
axum::extract::Form(api_req): axum::extract::Form<UserApiRequest>,
|
||||
api_form_req: Result<axum::extract::Form<UserApiRequest>, axum::extract::rejection::FormRejection>,
|
||||
) -> impl IntoResponse + Send {
|
||||
let form_req = api_form_req.as_ref().ok().map(|form| &form.0);
|
||||
let api_req = UserApiRequest::merge_query_over_form(&api_query_req, form_req);
|
||||
m3u_api(&api_req, &app_state).await.into_response()
|
||||
}
|
||||
|
||||
@@ -389,3 +392,43 @@ pub fn m3u_api_register() -> axum::Router<Arc<AppState>> {
|
||||
axum::routing::get(m3u_api_resource),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::model::UserApiRequest;
|
||||
|
||||
#[test]
|
||||
fn post_query_only_request_prefers_query_when_form_is_missing() {
|
||||
let api_query_req = UserApiRequest {
|
||||
username: String::from("query-user"),
|
||||
password: String::from("query-pass"),
|
||||
content_type: String::from("m3u_plus"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
|
||||
let api_req = UserApiRequest::merge_query_over_form(&api_query_req, None);
|
||||
|
||||
assert_eq!(api_req.username, "query-user");
|
||||
assert_eq!(api_req.password, "query-pass");
|
||||
assert_eq!(api_req.content_type, "m3u_plus");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post_request_prefers_query_over_form() {
|
||||
let api_query_req = UserApiRequest {
|
||||
username: String::from("query-user"),
|
||||
content_type: String::from("query-type"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let form_req = UserApiRequest {
|
||||
username: String::from("form-user"),
|
||||
content_type: String::from("form-type"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
|
||||
let api_req = UserApiRequest::merge_query_over_form(&api_query_req, Some(&form_req));
|
||||
|
||||
assert_eq!(api_req.username, "query-user");
|
||||
assert_eq!(api_req.content_type, "query-type");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,16 +686,6 @@ create_xtream_player_api_resource!(xtream_player_api_live_resource, ApiStreamCon
|
||||
create_xtream_player_api_resource!(xtream_player_api_series_resource, ApiStreamContext::Series);
|
||||
create_xtream_player_api_resource!(xtream_player_api_movie_resource, ApiStreamContext::Movie);
|
||||
|
||||
fn get_non_empty<'a>(first: &'a str, second: &'a str, third: &'a str) -> &'a str {
|
||||
if !first.is_empty() {
|
||||
first
|
||||
} else if !second.is_empty() {
|
||||
second
|
||||
} else {
|
||||
third
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||
struct XtreamApiTimeShiftRequest {
|
||||
username: String,
|
||||
@@ -708,40 +698,51 @@ struct XtreamApiTimeShiftRequest {
|
||||
async fn xtream_player_api_timeshift_stream(
|
||||
fingerprint: Fingerprint,
|
||||
req_headers: HeaderMap,
|
||||
axum::extract::Query(mut api_req): axum::extract::Query<UserApiRequest>,
|
||||
axum::extract::Query(api_query_req): axum::extract::Query<UserApiRequest>,
|
||||
axum::extract::Path(timeshift_request): axum::extract::Path<XtreamApiTimeShiftRequest>,
|
||||
axum::extract::State(app_state): axum::extract::State<Arc<AppState>>,
|
||||
axum::extract::Form(api_form_req): axum::extract::Form<UserApiRequest>,
|
||||
api_form_req: Result<axum::extract::Form<UserApiRequest>, axum::extract::rejection::FormRejection>,
|
||||
) -> impl IntoResponse + Send {
|
||||
let username = get_non_empty(×hift_request.username, &api_req.username, &api_form_req.username).to_string();
|
||||
let password = get_non_empty(×hift_request.password, &api_req.password, &api_form_req.password).to_string();
|
||||
let stream_id =
|
||||
get_non_empty(×hift_request.stream_id, &api_req.stream_id, &api_form_req.stream_id).to_string();
|
||||
let duration = get_non_empty(×hift_request.duration, &api_req.duration, &api_form_req.duration);
|
||||
let start_time = get_non_empty(×hift_request.start, &api_req.start, &api_form_req.start);
|
||||
let form_req = api_form_req.as_ref().ok().map(|form| &form.0);
|
||||
let query_req = UserApiRequest::merge_query_over_form(&api_query_req, form_req);
|
||||
let path_req = UserApiRequest {
|
||||
username: timeshift_request.username,
|
||||
password: timeshift_request.password,
|
||||
duration: timeshift_request.duration,
|
||||
start: timeshift_request.start,
|
||||
stream_id: timeshift_request.stream_id,
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let api_req = UserApiRequest::merge_prefer_primary(&path_req, &query_req);
|
||||
|
||||
let auth_status = app_state.app_config.get_auth_error_status();
|
||||
let (user, target) = try_option_forbidden!(
|
||||
get_user_target_by_credentials(&username, &password, &api_form_req, &app_state),
|
||||
get_user_target_by_credentials(&api_req.username, &api_req.password, &api_req, &app_state),
|
||||
auth_status,
|
||||
false,
|
||||
format!("Could not find any user {username}")
|
||||
format!("Could not find any user {}", api_req.username)
|
||||
);
|
||||
|
||||
let epg_timeshift = parse_timeshift(user.epg_request_timeshift.as_deref());
|
||||
let start = apply_timeshift(start_time, &epg_timeshift);
|
||||
let action_path = if start.is_empty() { format!("{duration}/{start_time}") } else { format!("{duration}/{start}") };
|
||||
|
||||
api_req.username.clone_from(&username);
|
||||
api_req.password.clone_from(&password);
|
||||
api_req.stream_id.clone_from(&stream_id);
|
||||
let start = apply_timeshift(&api_req.start, &epg_timeshift);
|
||||
let action_path = if start.is_empty() {
|
||||
format!("{}/{}", api_req.duration, api_req.start)
|
||||
} else {
|
||||
format!("{}/{}", api_req.duration, start)
|
||||
};
|
||||
|
||||
xtream_player_api_stream(
|
||||
&fingerprint,
|
||||
&req_headers,
|
||||
&app_state,
|
||||
&api_req,
|
||||
ApiStreamRequest::from(ApiStreamContext::Timeshift, &username, &password, &stream_id, &action_path),
|
||||
ApiStreamRequest::from(
|
||||
ApiStreamContext::Timeshift,
|
||||
&api_req.username,
|
||||
&api_req.password,
|
||||
&api_req.stream_id,
|
||||
&action_path,
|
||||
),
|
||||
Some((user, target)),
|
||||
)
|
||||
.await
|
||||
@@ -753,41 +754,48 @@ async fn xtream_player_api_timeshift_query_stream(
|
||||
req_headers: HeaderMap,
|
||||
axum::extract::Query(api_query_req): axum::extract::Query<UserApiRequest>,
|
||||
axum::extract::State(app_state): axum::extract::State<Arc<AppState>>,
|
||||
axum::extract::Form(api_form_req): axum::extract::Form<UserApiRequest>,
|
||||
api_form_req: Result<axum::extract::Form<UserApiRequest>, axum::extract::rejection::FormRejection>,
|
||||
) -> impl IntoResponse + Send {
|
||||
let username = get_non_empty(&api_query_req.username, &api_form_req.username, "");
|
||||
let password = get_non_empty(&api_query_req.password, &api_form_req.password, "");
|
||||
let stream_id = get_non_empty(&api_query_req.stream, &api_form_req.stream, "");
|
||||
let duration = get_non_empty(&api_query_req.duration, &api_form_req.duration, "");
|
||||
let start_time = get_non_empty(&api_query_req.start, &api_form_req.start, "");
|
||||
let form_req = api_form_req.as_ref().ok().map(|form| &form.0);
|
||||
let api_req = UserApiRequest::merge_query_over_form(&api_query_req, form_req);
|
||||
|
||||
if username.is_empty()
|
||||
|| password.is_empty()
|
||||
|| stream_id.is_empty()
|
||||
|| duration.is_empty()
|
||||
|| start_time.is_empty()
|
||||
if api_req.username.is_empty()
|
||||
|| api_req.password.is_empty()
|
||||
|| api_req.stream.is_empty()
|
||||
|| api_req.duration.is_empty()
|
||||
|| api_req.start.is_empty()
|
||||
{
|
||||
return axum::http::StatusCode::BAD_REQUEST.into_response();
|
||||
}
|
||||
|
||||
let auth_status = app_state.app_config.get_auth_error_status();
|
||||
let (user, target) = try_option_forbidden!(
|
||||
get_user_target_by_credentials(username, password, &api_query_req, &app_state),
|
||||
get_user_target_by_credentials(&api_req.username, &api_req.password, &api_req, &app_state),
|
||||
auth_status,
|
||||
false,
|
||||
format!("Could not find any user {username}")
|
||||
format!("Could not find any user {}", api_req.username)
|
||||
);
|
||||
|
||||
let epg_timeshift = parse_timeshift(user.epg_request_timeshift.as_deref());
|
||||
let start = apply_timeshift(start_time, &epg_timeshift);
|
||||
let action_path = if start.is_empty() { format!("{duration}/{start_time}") } else { format!("{duration}/{start}") };
|
||||
let start = apply_timeshift(&api_req.start, &epg_timeshift);
|
||||
let action_path = if start.is_empty() {
|
||||
format!("{}/{}", api_req.duration, api_req.start)
|
||||
} else {
|
||||
format!("{}/{}", api_req.duration, start)
|
||||
};
|
||||
|
||||
xtream_player_api_stream(
|
||||
&fingerprint,
|
||||
&req_headers,
|
||||
&app_state,
|
||||
&api_query_req,
|
||||
ApiStreamRequest::from(ApiStreamContext::Timeshift, username, password, stream_id, &action_path),
|
||||
&api_req,
|
||||
ApiStreamRequest::from(
|
||||
ApiStreamContext::Timeshift,
|
||||
&api_req.username,
|
||||
&api_req.password,
|
||||
&api_req.stream,
|
||||
&action_path,
|
||||
),
|
||||
Some((user, target)),
|
||||
)
|
||||
.await
|
||||
@@ -1305,8 +1313,11 @@ async fn xtream_player_api_get(
|
||||
|
||||
async fn xtream_player_api_post(
|
||||
axum::extract::State(app_state): axum::extract::State<Arc<AppState>>,
|
||||
axum::extract::Form(api_req): axum::extract::Form<UserApiRequest>,
|
||||
axum::extract::Query(api_query_req): axum::extract::Query<UserApiRequest>,
|
||||
api_form_req: Result<axum::extract::Form<UserApiRequest>, axum::extract::rejection::FormRejection>,
|
||||
) -> impl IntoResponse + Send {
|
||||
let form_req = api_form_req.as_ref().ok().map(|form| &form.0);
|
||||
let api_req = UserApiRequest::merge_query_over_form(&api_query_req, form_req);
|
||||
xtream_player_api(api_req, &app_state).await
|
||||
}
|
||||
|
||||
@@ -1398,3 +1409,167 @@ pub fn xtream_api_register() -> axum::Router<Arc<AppState>> {
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::model::UserApiRequest;
|
||||
use super::XtreamApiTimeShiftRequest;
|
||||
|
||||
#[test]
|
||||
fn post_query_only_request_prefers_query_when_form_is_missing() {
|
||||
let api_query_req = UserApiRequest {
|
||||
username: String::from("query-user"),
|
||||
password: String::from("query-pass"),
|
||||
action: String::from("get_live_streams"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
|
||||
let api_req = UserApiRequest::merge_query_over_form(&api_query_req, None);
|
||||
|
||||
assert_eq!(api_req.username, "query-user");
|
||||
assert_eq!(api_req.password, "query-pass");
|
||||
assert_eq!(api_req.action, "get_live_streams");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post_request_prefers_query_over_form() {
|
||||
let api_query_req = UserApiRequest {
|
||||
username: String::from("query-user"),
|
||||
action: String::from("query-action"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let form_req = UserApiRequest {
|
||||
username: String::from("form-user"),
|
||||
action: String::from("form-action"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
|
||||
let api_req = UserApiRequest::merge_query_over_form(&api_query_req, Some(&form_req));
|
||||
|
||||
assert_eq!(api_req.username, "query-user");
|
||||
assert_eq!(api_req.action, "query-action");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timeshift_query_request_prefers_query_when_form_is_missing() {
|
||||
let api_query_req = UserApiRequest {
|
||||
username: String::from("query-user"),
|
||||
password: String::from("query-pass"),
|
||||
stream: String::from("42"),
|
||||
duration: String::from("60"),
|
||||
start: String::from("2024-01-01:00-00"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let api_req = UserApiRequest::merge_query_over_form(&api_query_req, None);
|
||||
|
||||
assert_eq!(api_req.username, "query-user");
|
||||
assert_eq!(api_req.password, "query-pass");
|
||||
assert_eq!(api_req.stream, "42");
|
||||
assert_eq!(api_req.duration, "60");
|
||||
assert_eq!(api_req.start, "2024-01-01:00-00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timeshift_query_request_prefers_query_over_form() {
|
||||
let api_query_req = UserApiRequest {
|
||||
username: String::from("query-user"),
|
||||
stream: String::from("42"),
|
||||
duration: String::from("60"),
|
||||
start: String::from("2024-01-01:00-00"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let form_req = UserApiRequest {
|
||||
username: String::from("form-user"),
|
||||
stream: String::from("99"),
|
||||
duration: String::from("10"),
|
||||
start: String::from("form-start"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
|
||||
let api_req = UserApiRequest::merge_query_over_form(&api_query_req, Some(&form_req));
|
||||
|
||||
assert_eq!(api_req.username, "query-user");
|
||||
assert_eq!(api_req.stream, "42");
|
||||
assert_eq!(api_req.duration, "60");
|
||||
assert_eq!(api_req.start, "2024-01-01:00-00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timeshift_path_request_prefers_query_when_form_is_missing() {
|
||||
let timeshift_request = XtreamApiTimeShiftRequest {
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
duration: String::new(),
|
||||
start: String::new(),
|
||||
stream_id: String::new(),
|
||||
};
|
||||
let api_query_req = UserApiRequest {
|
||||
username: String::from("query-user"),
|
||||
password: String::from("query-pass"),
|
||||
stream_id: String::from("42"),
|
||||
duration: String::from("60"),
|
||||
start: String::from("2024-01-01:00-00"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let query_req = UserApiRequest::merge_query_over_form(&api_query_req, None);
|
||||
let path_req = UserApiRequest {
|
||||
username: timeshift_request.username,
|
||||
password: timeshift_request.password,
|
||||
duration: timeshift_request.duration,
|
||||
start: timeshift_request.start,
|
||||
stream_id: timeshift_request.stream_id,
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let api_req = UserApiRequest::merge_prefer_primary(&path_req, &query_req);
|
||||
|
||||
assert_eq!(api_req.username, "query-user");
|
||||
assert_eq!(api_req.password, "query-pass");
|
||||
assert_eq!(api_req.stream_id, "42");
|
||||
assert_eq!(api_req.duration, "60");
|
||||
assert_eq!(api_req.start, "2024-01-01:00-00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timeshift_path_request_prefers_path_over_query_and_form() {
|
||||
let timeshift_request = XtreamApiTimeShiftRequest {
|
||||
username: String::from("path-user"),
|
||||
password: String::from("path-pass"),
|
||||
duration: String::from("120"),
|
||||
start: String::from("path-start"),
|
||||
stream_id: String::from("7"),
|
||||
};
|
||||
let api_query_req = UserApiRequest {
|
||||
username: String::from("query-user"),
|
||||
password: String::from("query-pass"),
|
||||
stream_id: String::from("42"),
|
||||
duration: String::from("60"),
|
||||
start: String::from("query-start"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let form_req = UserApiRequest {
|
||||
username: String::from("form-user"),
|
||||
password: String::from("form-pass"),
|
||||
stream_id: String::from("99"),
|
||||
duration: String::from("10"),
|
||||
start: String::from("form-start"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
|
||||
let query_req = UserApiRequest::merge_query_over_form(&api_query_req, Some(&form_req));
|
||||
let path_req = UserApiRequest {
|
||||
username: timeshift_request.username,
|
||||
password: timeshift_request.password,
|
||||
duration: timeshift_request.duration,
|
||||
start: timeshift_request.start,
|
||||
stream_id: timeshift_request.stream_id,
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let api_req = UserApiRequest::merge_prefer_primary(&path_req, &query_req);
|
||||
|
||||
assert_eq!(api_req.username, "path-user");
|
||||
assert_eq!(api_req.password, "path-pass");
|
||||
assert_eq!(api_req.stream_id, "7");
|
||||
assert_eq!(api_req.duration, "120");
|
||||
assert_eq!(api_req.start, "path-start");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
|
||||
pub struct UserApiRequest {
|
||||
#[serde(default)]
|
||||
pub username: String,
|
||||
@@ -31,6 +31,37 @@ pub struct UserApiRequest {
|
||||
}
|
||||
|
||||
impl UserApiRequest {
|
||||
pub fn merge_prefer_primary(primary: &Self, fallback: &Self) -> Self {
|
||||
fn pick(primary: &str, fallback: &str) -> String {
|
||||
if primary.trim().is_empty() {
|
||||
fallback.to_string()
|
||||
} else {
|
||||
primary.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
username: pick(&primary.username, &fallback.username),
|
||||
password: pick(&primary.password, &fallback.password),
|
||||
token: pick(&primary.token, &fallback.token),
|
||||
action: pick(&primary.action, &fallback.action),
|
||||
series_id: pick(&primary.series_id, &fallback.series_id),
|
||||
vod_id: pick(&primary.vod_id, &fallback.vod_id),
|
||||
stream_id: pick(&primary.stream_id, &fallback.stream_id),
|
||||
category_id: pick(&primary.category_id, &fallback.category_id),
|
||||
limit: pick(&primary.limit, &fallback.limit),
|
||||
start: pick(&primary.start, &fallback.start),
|
||||
end: pick(&primary.end, &fallback.end),
|
||||
stream: pick(&primary.stream, &fallback.stream),
|
||||
duration: pick(&primary.duration, &fallback.duration),
|
||||
content_type: pick(&primary.content_type, &fallback.content_type),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn merge_query_over_form(query: &Self, form: Option<&Self>) -> Self {
|
||||
form.map_or_else(|| query.clone(), |form_req| Self::merge_prefer_primary(query, form_req))
|
||||
}
|
||||
|
||||
pub fn get_limit(&self) -> u32 {
|
||||
if self.limit.is_empty() {
|
||||
0
|
||||
@@ -39,3 +70,104 @@ impl UserApiRequest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::UserApiRequest;
|
||||
|
||||
#[test]
|
||||
fn merge_prefer_primary_uses_fallback_for_empty_fields() {
|
||||
let primary = UserApiRequest {
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
action: String::from("get_live_categories"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let fallback = UserApiRequest {
|
||||
username: String::from("xtr"),
|
||||
password: String::from("xtr"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
|
||||
let merged = UserApiRequest::merge_prefer_primary(&primary, &fallback);
|
||||
|
||||
assert_eq!(merged.username, "xtr");
|
||||
assert_eq!(merged.password, "xtr");
|
||||
assert_eq!(merged.action, "get_live_categories");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_prefer_primary_uses_fallback_for_whitespace_only_fields() {
|
||||
let primary = UserApiRequest {
|
||||
username: String::from(" "),
|
||||
password: String::from("\t"),
|
||||
token: String::from("\n"),
|
||||
action: String::from(" "),
|
||||
series_id: String::from(" "),
|
||||
vod_id: String::from(" "),
|
||||
stream_id: String::from(" "),
|
||||
category_id: String::from(" "),
|
||||
limit: String::from(" "),
|
||||
start: String::from(" "),
|
||||
end: String::from(" "),
|
||||
stream: String::from(" "),
|
||||
duration: String::from(" "),
|
||||
content_type: String::from(" "),
|
||||
};
|
||||
let fallback = UserApiRequest {
|
||||
username: String::from("user"),
|
||||
password: String::from("pass"),
|
||||
token: String::from("token"),
|
||||
action: String::from("action"),
|
||||
series_id: String::from("series"),
|
||||
vod_id: String::from("vod"),
|
||||
stream_id: String::from("stream"),
|
||||
category_id: String::from("category"),
|
||||
limit: String::from("10"),
|
||||
start: String::from("100"),
|
||||
end: String::from("200"),
|
||||
stream: String::from("300"),
|
||||
duration: String::from("60"),
|
||||
content_type: String::from("m3u_plus"),
|
||||
};
|
||||
|
||||
let merged = UserApiRequest::merge_prefer_primary(&primary, &fallback);
|
||||
|
||||
assert_eq!(merged.username, "user");
|
||||
assert_eq!(merged.password, "pass");
|
||||
assert_eq!(merged.token, "token");
|
||||
assert_eq!(merged.action, "action");
|
||||
assert_eq!(merged.series_id, "series");
|
||||
assert_eq!(merged.vod_id, "vod");
|
||||
assert_eq!(merged.stream_id, "stream");
|
||||
assert_eq!(merged.category_id, "category");
|
||||
assert_eq!(merged.limit, "10");
|
||||
assert_eq!(merged.start, "100");
|
||||
assert_eq!(merged.end, "200");
|
||||
assert_eq!(merged.stream, "300");
|
||||
assert_eq!(merged.duration, "60");
|
||||
assert_eq!(merged.content_type, "m3u_plus");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_query_over_form_prefers_query_fields() {
|
||||
let query = UserApiRequest {
|
||||
username: String::from("query-user"),
|
||||
token: String::from("query-token"),
|
||||
action: String::from("query-action"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
let form = UserApiRequest {
|
||||
username: String::from("form-user"),
|
||||
token: String::from("form-token"),
|
||||
action: String::from("form-action"),
|
||||
..UserApiRequest::default()
|
||||
};
|
||||
|
||||
let merged = UserApiRequest::merge_query_over_form(&query, Some(&form));
|
||||
|
||||
assert_eq!(merged.username, "query-user");
|
||||
assert_eq!(merged.token, "query-token");
|
||||
assert_eq!(merged.action, "query-action");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user