use std::any::type_name;
use nextcloud_sdk_core::{ApiPath, DecodeError, Error, HttpMethod, Response, StatusError};
use serde::de::DeserializeOwned;
use crate::envelope::{OcsEnvelope, OcsStatus, VerdictEnvelope};
use crate::error::StatusCodeError;
pub(crate) fn decode_response<Res: DeserializeOwned>(
method: HttpMethod,
path: ApiPath,
response: &Response,
) -> Result<Res, Error> {
if !response.status().is_success() {
tracing::warn!(
method = ?method,
path = %path.as_str(),
status = %response.status(),
"OCS response received non-success HTTP status"
);
return Err(status_error(method, path, response));
}
if response.body().is_empty() {
return serde_json::from_str::<Res>("null")
.map_err(|cause| DecodeError::new(type_name::<OcsEnvelope<Res>>(), cause))
.map_err(Error::Decode);
}
let verdict: VerdictEnvelope = serde_json::from_slice(response.body())
.map_err(|cause| DecodeError::new(type_name::<OcsEnvelope<Res>>(), cause))
.map_err(Error::Decode)?;
match verdict.ocs.meta.verdict() {
Some(OcsStatus::Ok) => {
tracing::trace!(
method = ?method,
path = %path.as_str(),
statuscode = verdict.ocs.meta.statuscode(),
"OCS response statuscode verified successfully"
);
}
Some(OcsStatus::Failure) => {
tracing::error!(
method = ?method,
path = %path.as_str(),
statuscode = verdict.ocs.meta.statuscode(),
message = %verdict.ocs.meta.message(),
"OCS response envelope reported failure verdict"
);
return Err(status_error(method, path, response));
}
None => {
tracing::error!(
method = ?method,
path = %path.as_str(),
statuscode = verdict.ocs.meta.statuscode(),
"OCS response envelope missing valid verdict"
);
return Err(Error::Decode(DecodeError::new(
type_name::<OcsEnvelope<Res>>(),
StatusCodeError::new(verdict.ocs.meta.statuscode()),
)));
}
}
match serde_json::from_slice::<OcsEnvelope<Res>>(response.body()) {
Ok(envelope) => Ok(envelope.into_data()),
Err(cause) => {
if let Ok(value) = serde_json::from_str::<Res>("null") {
Ok(value)
} else {
Err(Error::Decode(DecodeError::new(
type_name::<OcsEnvelope<Res>>(),
cause,
)))
}
}
}
}
pub(crate) fn status_error(method: HttpMethod, path: ApiPath, response: &Response) -> Error {
Error::Status(StatusError::new(
method.as_str(),
path,
response.status(),
response.body(),
))
}