blitz_traits/
net.rs

1//! Abstractions of networking so that custom networking implementations can be provided
2
3pub use bytes::Bytes;
4pub use http::{self, HeaderMap, Method};
5use serde::{
6    Serialize,
7    ser::{SerializeSeq, SerializeTuple},
8};
9use std::{ops::Deref, path::PathBuf, sync::Arc};
10pub use url::Url;
11
12pub type SharedProvider<D> = Arc<dyn NetProvider<D>>;
13pub type BoxedHandler<D> = Box<dyn NetHandler<D>>;
14pub type SharedCallback<D> = Arc<dyn NetCallback<D>>;
15
16/// A type that fetches resources for a Document.
17///
18/// This may be over the network via http(s), via the filesystem, or some other method.
19pub trait NetProvider<Data>: Send + Sync + 'static {
20    fn fetch(&self, doc_id: usize, request: Request, handler: BoxedHandler<Data>);
21}
22
23/// A type that parses raw bytes from a network request into a Data and then calls
24/// the NetCallack with the result.
25pub trait NetHandler<Data>: Send + Sync + 'static {
26    fn bytes(self: Box<Self>, doc_id: usize, bytes: Bytes, callback: SharedCallback<Data>);
27}
28
29/// A type which accepts the parsed result of a network request and sends it back to the Document
30/// (or does arbitrary things with it)
31pub trait NetCallback<Data>: Send + Sync + 'static {
32    fn call(&self, doc_id: usize, result: Result<Data, Option<String>>);
33}
34
35impl<D, F: Fn(usize, Result<D, Option<String>>) + Send + Sync + 'static> NetCallback<D> for F {
36    fn call(&self, doc_id: usize, result: Result<D, Option<String>>) {
37        self(doc_id, result)
38    }
39}
40
41#[non_exhaustive]
42#[derive(Debug)]
43/// A request type loosely representing <https://fetch.spec.whatwg.org/#requests>
44pub struct Request {
45    pub url: Url,
46    pub method: Method,
47    pub content_type: String,
48    pub headers: HeaderMap,
49    pub body: Body,
50}
51impl Request {
52    /// A get request to the specified Url and an empty body
53    pub fn get(url: Url) -> Self {
54        Self {
55            url,
56            method: Method::GET,
57            content_type: String::new(),
58            headers: HeaderMap::new(),
59            body: Body::Empty,
60        }
61    }
62}
63
64#[derive(Debug, Clone)]
65pub enum Body {
66    Bytes(Bytes),
67    Form(FormData),
68    Empty,
69}
70
71/// A list of form entries used for form submission
72#[derive(Debug, Clone, PartialEq, Default)]
73pub struct FormData(pub Vec<Entry>);
74impl FormData {
75    /// Creates a new empty FormData
76    pub fn new() -> Self {
77        FormData(Vec::new())
78    }
79}
80impl Serialize for FormData {
81    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
82    where
83        S: serde::Serializer,
84    {
85        let mut seq_serializer = serializer.serialize_seq(Some(self.len()))?;
86        for entry in &self.0 {
87            seq_serializer.serialize_element(entry)?;
88        }
89        seq_serializer.end()
90    }
91}
92impl Deref for FormData {
93    type Target = Vec<Entry>;
94
95    fn deref(&self) -> &Self::Target {
96        &self.0
97    }
98}
99
100/// A single form entry consisting of a name and value
101#[derive(Debug, Clone, PartialEq)]
102pub struct Entry {
103    pub name: String,
104    pub value: EntryValue,
105}
106impl Serialize for Entry {
107    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
108    where
109        S: serde::Serializer,
110    {
111        let mut serializer = serializer.serialize_tuple(2)?;
112        serializer.serialize_element(&self.name)?;
113        match &self.value {
114            EntryValue::String(s) => serializer.serialize_element(s)?,
115            EntryValue::File(p) => serializer.serialize_element(p.to_str().unwrap_or_default())?,
116            EntryValue::EmptyFile => serializer.serialize_element("")?,
117        }
118        serializer.end()
119    }
120}
121
122#[derive(Debug, Clone, PartialEq)]
123pub enum EntryValue {
124    String(String),
125    File(PathBuf),
126    EmptyFile,
127}
128impl AsRef<str> for EntryValue {
129    fn as_ref(&self) -> &str {
130        match self {
131            EntryValue::String(s) => s,
132            EntryValue::File(p) => p.to_str().unwrap_or_default(),
133            EntryValue::EmptyFile => "",
134        }
135    }
136}
137
138impl From<&str> for EntryValue {
139    fn from(value: &str) -> Self {
140        EntryValue::String(value.to_string())
141    }
142}
143impl From<PathBuf> for EntryValue {
144    fn from(value: PathBuf) -> Self {
145        EntryValue::File(value)
146    }
147}
148
149/// A default noop NetProvider
150#[derive(Default)]
151pub struct DummyNetProvider;
152impl<D: Send + Sync + 'static> NetProvider<D> for DummyNetProvider {
153    fn fetch(&self, _doc_id: usize, _request: Request, _handler: BoxedHandler<D>) {}
154}
155
156/// A default noop NetCallback
157#[derive(Default)]
158pub struct DummyNetCallback;
159impl<D: Send + Sync + 'static> NetCallback<D> for DummyNetCallback {
160    fn call(&self, _doc_id: usize, _result: Result<D, Option<String>>) {}
161}