ft_sdk/
data.rs

1pub type Result = std::result::Result<ft_sdk::chr::CHR<Output>, ft_sdk::Error>;
2
3#[derive(Debug)]
4pub enum Output {
5    Json(serde_json::Value),
6    Binary(Binary),
7    /// This variant is intended for setting cookies and then redirecting the browser.
8    Redirect(String, http::HeaderValue),
9}
10
11#[derive(Debug)]
12pub struct Binary {
13    pub file_name: Option<String>,
14    pub content: bytes::Bytes,
15    pub content_type: String,
16}
17
18pub(crate) fn binary_response(
19    binary: Binary,
20) -> std::result::Result<::http::Response<bytes::Bytes>, ft_sdk::Error> {
21    let mut response_builder = http::Response::builder()
22        .status(200)
23        .header("Content-Type", binary.content_type.as_str());
24
25    // Add the binary as attachment, indicating it should be downloaded
26    if let Some(filename) = binary.file_name {
27        response_builder = response_builder.header(
28            "Content-Disposition",
29            format!("attachment; filename=\"{filename}\"").as_str(),
30        )
31    }
32
33    Ok(response_builder.body(binary.content)?)
34}
35
36impl From<ft_sdk::chr::CHR<Output>>
37    for std::result::Result<http::Response<bytes::Bytes>, ft_sdk::Error>
38{
39    fn from(
40        ft_sdk::chr::CHR {
41            cookies,
42            headers,
43            response,
44        }: ft_sdk::chr::CHR<Output>,
45    ) -> Self {
46        let response = match response {
47            Output::Json(json_value) => ft_sdk::json(json_value),
48            Output::Binary(binary) => binary_response(binary),
49            Output::Redirect(url, cookie) => Ok(http::Response::builder()
50                .status(200)
51                .header(http::header::SET_COOKIE, cookie)
52                .header(http::header::CONTENT_TYPE, "text/html; charset=utf-8")
53                .body(format!("<meta http-equiv='refresh' content='0; url={url}' />").into())?),
54        }?;
55        ft_sdk::chr::chr(cookies, headers, response)
56    }
57}
58
59/// Creates a response that instructs the browser to store in downloads the binary content provided.
60///
61/// # Parameters
62/// - `filename`: An optional `String` representing the name of the file. If provided, the response
63///   will add `Content-Disposition: attachment; filename="{filename}"` as header, indicating the
64///   binary should be downloaded and this name will be used as the filename for the download.
65/// - `content`: A `bytes::Bytes` object containing the binary content.
66/// - `content_type`: An `AsRef<str>` (&str/String) specifying the MIME type of the content
67///   (e.g., `application/pdf`, `image/png`).
68///
69/// # Example
70///
71/// ```rust
72/// let content = bytes::Bytes::from("This is the content of the file.");
73///
74/// ft_sdk::data::download("example.txt", content, "text/plain").unwrap();
75/// ```
76pub fn download<S1: AsRef<str>, S2: AsRef<str>>(
77    file_name: S1,
78    content: bytes::Bytes,
79    content_type: S2,
80) -> Result {
81    Ok(ft_sdk::chr::CHR::new(Output::Binary(Binary {
82        file_name: Some(file_name.as_ref().to_string()),
83        content,
84        content_type: content_type.as_ref().to_string(),
85    })))
86}
87
88/// Creates a binary response for serving binary data over HTTP.
89///
90/// # Parameters
91/// - `content`: A `bytes::Bytes` object containing the binary content.
92/// - `content_type`: An `AsRef<str>` (&str/String) specifying the MIME type of the content
93///   (e.g., `application/pdf`, `image/png`).
94///
95/// # Example
96///
97/// ```rust
98/// let content = bytes::Bytes::from("This is the content of the file.");
99///
100/// ft_sdk::data::binary(content, "text/plain").unwrap();
101/// ```
102pub fn binary<S: AsRef<str>>(content: bytes::Bytes, content_type: S) -> Result {
103    Ok(ft_sdk::chr::CHR::new(Output::Binary(Binary {
104        file_name: None,
105        content,
106        content_type: content_type.as_ref().to_string(),
107    })))
108}
109
110/// Set a cookie and redirect using 200 response.
111///
112/// Adding cookie with redirect headers does not work across browsers. This helper creates
113/// a 200-OK response, with an HTML meta-refresh tag to redirect the browser.
114///
115/// ```rust
116///  let cookie = cookie::Cookie::build((ft_sdk::auth::SESSION_KEY, "some-uniq-key"))
117///         .domain("127.0.0.1")
118///         .path("")
119///         .max_age(cookie::time::Duration::seconds(34560000))
120///         .same_site(cookie::SameSite::Strict)
121///         .build();
122/// ft_sdk::data::browser_redirect_with_cookie("/", http::HeaderValue::from_str(cookie.to_string().as_str()).unwrap());
123/// ```
124pub fn browser_redirect_with_cookie<S: AsRef<str>>(url: S, c: http::HeaderValue) -> Result {
125    Ok(ft_sdk::chr::CHR::new(Output::Redirect(
126        url.as_ref().to_string(),
127        c,
128    )))
129}
130
131pub fn json<T: serde::Serialize>(t: T) -> Result {
132    Ok(ft_sdk::chr::CHR::new(Output::Json(serde_json::to_value(
133        t,
134    )?)))
135}
136
137pub fn api_ok<T: serde::Serialize>(t: T) -> Result {
138    Ok(ft_sdk::chr::CHR::new(Output::Json(
139        serde_json::json!({"data": serde_json::to_value(t)?, "success": true }),
140    )))
141}
142
143pub fn api_error(errors: std::collections::HashMap<String, String>) -> Result {
144    Ok(ft_sdk::chr::CHR::new(Output::Json(
145        serde_json::json!({"errors": serde_json::to_value(errors)?, "success": false }),
146    )))
147}