// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::marker::PhantomData; use axum::body::Body; use axum::response::IntoResponse; use axum::response::Response; use axum_extra::headers::HeaderName; use serde::de::DeserializeOwned; use serde::Serialize; use crate::prelude::*; use crate::sync::collection::upload::UploadResponse; use crate::sync::error::HttpResult; use crate::sync::error::OrHttpErr; use crate::sync::request::header_and_stream::encode_zstd_body; use crate::sync::version::SyncVersion; pub static ORIGINAL_SIZE: HeaderName = HeaderName::from_static("anki-original-size"); /// Stores the data returned from a sync request, and the type /// it represents. Given a SyncResponse, you can get a Foo /// struct via .json(), except for uploads/downloads. #[derive(Debug)] pub struct SyncResponse { pub data: Vec, json_output_type: PhantomData, } impl SyncResponse { pub fn from_vec(data: Vec) -> SyncResponse { SyncResponse { data, json_output_type: Default::default(), } } pub fn make_response(self, sync_version: SyncVersion) -> Response { if sync_version.is_zstd() { let header = (&ORIGINAL_SIZE, self.data.len().to_string()); let body = Body::from_stream(encode_zstd_body(self.data)); ([header], body).into_response() } else { self.data.into_response() } } } impl SyncResponse { // Unfortunately the sync protocol sends this as a bare string // instead of JSON. pub fn upload_response(&self) -> UploadResponse { let resp = String::from_utf8_lossy(&self.data); match resp.as_ref() { "OK" => UploadResponse::Ok, other => UploadResponse::Err(other.into()), } } pub fn from_upload_response(resp: UploadResponse) -> Self { let text = match resp { UploadResponse::Ok => "OK".into(), UploadResponse::Err(other) => other, }; SyncResponse::from_vec(text.into_bytes()) } } impl SyncResponse where T: Serialize, { pub fn try_from_obj(obj: T) -> HttpResult> { let data = serde_json::to_vec(&obj).or_internal_err("couldn't serialize object")?; Ok(SyncResponse::from_vec(data)) } } impl SyncResponse where T: DeserializeOwned, { pub fn json(&self) -> Result { serde_json::from_slice(&self.data).map_err(Into::into) } }