libworker 0.0.1

Idiomatic framework / abstractions over `edgeworker-sys` FFI bindings.
Documentation
use std::collections::HashMap;

use crate::error::Error;
use crate::Date;
use crate::DateInit;
use crate::Result;

use edgeworker_sys::{File as EdgeFile, FormData as EdgeFormData};

use js_sys::Array;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;

pub enum FormEntry {
    Field(String),
    File(File),
}

#[derive(Debug)]
pub struct FormData(EdgeFormData);

impl FormData {
    pub fn new() -> Self {
        Self(EdgeFormData::new().unwrap())
    }

    pub fn get(&self, name: &str) -> Option<FormEntry> {
        let val = self.0.get(name);
        if val.is_undefined() {
            return None;
        }

        if val.is_instance_of::<EdgeFile>() {
            return Some(FormEntry::File(File(val.into())));
        }

        if let Some(field) = val.as_string() {
            return Some(FormEntry::Field(field));
        }

        return None;
    }

    pub fn get_all(&self, name: &str) -> Option<Vec<FormEntry>> {
        let val = self.0.get_all(name);
        if val.is_undefined() {
            return None;
        }

        if Array::is_array(&val) {
            return Some(
                val.to_vec()
                    .into_iter()
                    .map(|val| {
                        if val.is_instance_of::<EdgeFile>() {
                            return FormEntry::File(File(val.into()));
                        }

                        return FormEntry::Field(val.as_string().unwrap_or_default());
                    })
                    .collect(),
            );
        }

        None
    }

    pub fn has(&self, name: &str) -> bool {
        self.0.has(name)
    }

    pub fn append(&mut self, name: &str, value: &str) -> Result<()> {
        self.0.append_with_str(name, value).map_err(Error::from)
    }

    pub fn set(&mut self, name: &str, value: &str) -> Result<()> {
        self.0.set_with_str(name, value).map_err(Error::from)
    }

    pub fn delete(&mut self, name: &str) {
        self.0.delete(name)
    }
}

impl From<JsValue> for FormData {
    fn from(val: JsValue) -> Self {
        FormData(val.into())
    }
}

impl From<HashMap<&dyn AsRef<&str>, &dyn AsRef<&str>>> for FormData {
    fn from(m: HashMap<&dyn AsRef<&str>, &dyn AsRef<&str>>) -> Self {
        let mut formdata = FormData::new();
        for (k, v) in m {
            // TODO: determine error case and consider how to handle
            formdata.set(k.as_ref(), v.as_ref()).unwrap();
        }
        formdata
    }
}

pub struct File(EdgeFile);

impl File {
    pub fn new(data: Vec<u8>, name: &str) -> Self {
        let arr = Array::new();
        for byte in data.into_iter() {
            arr.push(&byte.into());
        }

        let file = EdgeFile::new_with_u8_array_sequence(&JsValue::from(arr), name).unwrap();
        Self(file)
    }

    pub fn name(&self) -> String {
        self.0.name()
    }

    pub async fn bytes(&self) -> Result<Vec<u8>> {
        JsFuture::from(self.0.array_buffer())
            .await
            .map(|val| js_sys::Uint8Array::new(&val).to_vec())
            .map_err(|e| {
                Error::JsError(
                    e.as_string()
                        .unwrap_or_else(|| "failed to read array buffer from file".into()),
                )
            })
    }

    pub fn last_modified(&self) -> Date {
        DateInit::Millis(self.0.last_modified() as u64).into()
    }
}

impl From<EdgeFile> for File {
    fn from(file: EdgeFile) -> Self {
        Self(file)
    }
}