1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
//!  This crate provides rust library to easily compose WASM/WASI browser service worker.
//!  
//!  # General overview
//!
//!  ServiceWorker is a singleton which holds input and output file handles and
//!  owns worker via Handler trait. Worker is supposedly reactive, usually operating
//!  on incoming events (on_message) and posting messages to main browser application
//!  via ServiceWorker::post_message().
//!
//!  # Example usage:
//!  ```
//!  use wasi_worker::*;
//!
//!  struct MyWorker {}
//!  impl Handler for MyWorker {
//!    fn on_message(&self, msg: &[u8]) -> std::io::Result<()> {
//!      // Process incoming message
//!      println!("My Worker got message: {:?}", msg);
//!      Ok(())
//!    }
//!  }
//!
//!  fn main() {
//!    // In WASI setup with JS glue all output will be posted to memfs::/output.bin
//!    // In native OS to be able to run test from shell output goes to ./output.bin
//!    let opt = ServiceOptions::default().with_cleanup();
//!    let output_file = match &opt.output { FileOptions::File(path) => path.clone() };
//!    ServiceWorker::initialize(opt)
//!      .expect("ServiceWorker::initialize");
//!
//!    // Attach Agent to ServiceWorker as message handler singleton
//!    ServiceWorker::set_message_handler(Box::new(MyWorker {}));
//!
//!    // Send binary message to main browser application
//!    ServiceWorker::post_message(b"message")
//!      .expect("ServiceWorker.post_message");
//!  }
//!  ```
mod service;

pub use service::{Handler, ServiceWorker};

/// Instructs on file descriptor configuration for ServiceWorker
pub enum FileOptions {
    File(String),
}

/// Options for ServiceWorker
pub struct ServiceOptions {
    // TODO:  input: FileOptions,
    pub cleanup: bool,
    pub output: FileOptions,
}

impl ServiceOptions {
    pub fn with_cleanup(mut self) -> Self {
        self.cleanup = true;
        self
    }
}

impl Default for ServiceOptions {
    fn default() -> Self {
        Self {
            output: if cfg!(target_os = "wasi") {
                FileOptions::File("/output.bin".to_string())
            } else {
                FileOptions::File("./output.bin".to_string())
            },
            cleanup: false,
        }
    }
}

// This function will be called from worker.js on new message
// To operate it requires JS glue - see wasi-worker-cli
// Note: It will be substituted by poll_oneoff,
// though currently poll_oneoff does not transfer control
#[no_mangle]
pub extern "C" fn message_ready() -> usize {
    ServiceWorker::on_message().expect("ServiceWorker.on_message")
}


#[cfg(test)]
mod tests {
    use super::{FileOptions, ServiceOptions, ServiceWorker};

    #[test]
    fn cleanup() {
        {
            let opt = ServiceOptions {
                output: FileOptions::File("./testdata/output.bin".to_string()),
                cleanup: true,
            };
            ServiceWorker::initialize(opt).expect("ServiceWorker::initialize");
            std::fs::File::open("./testdata/output.bin")
                .expect("/testdata/output.bin should been created");
            ServiceWorker::kill();
        }
        std::fs::File::open("./testdata/output.bin")
            .expect_err("/testdata/output.bin should been cleaned up");
    }
}