use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
#[derive(Clone, Debug)]
pub struct CapabilityCtx {
pub req_id: u64,
}
pub trait OperationCapability: Send + 'static {
type Request: Serialize + for<'de> Deserialize<'de> + Send + 'static;
type Ok: Serialize + for<'de> Deserialize<'de> + Send + 'static;
type Err: Serialize + for<'de> Deserialize<'de> + Send + 'static;
}
#[derive(Copy, Clone)]
pub struct CapabilityType<C: OperationCapability> {
pub name: &'static str,
_marker: PhantomData<fn() -> C>,
}
impl<C: OperationCapability> CapabilityType<C> {
pub const fn new(name: &'static str) -> Self {
Self {
name,
_marker: PhantomData,
}
}
}
impl<C: OperationCapability> std::fmt::Debug for CapabilityType<C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CapabilityType")
.field("name", &self.name)
.finish()
}
}
impl<C: OperationCapability> PartialEq for CapabilityType<C> {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl<C: OperationCapability> Eq for CapabilityType<C> {}
impl<C: OperationCapability> std::hash::Hash for CapabilityType<C> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct OperationCapabilityInvocation {
pub capability_name: String,
pub request: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum CapabilityInvocationPayload {
Operation(OperationCapabilityInvocation),
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct OpenUrlRequest {
pub url: String,
pub in_app: bool,
}
pub struct OpenUrlCapability;
impl OperationCapability for OpenUrlCapability {
type Request = OpenUrlRequest;
type Ok = ();
type Err = String;
}
pub const OPEN_URL: CapabilityType<OpenUrlCapability> = CapabilityType::new("fission.ui.open_url");
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct PickOpenFilesRequest {
pub allow_multiple: bool,
pub mime_types: Vec<String>,
pub extensions: Vec<String>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct PickedFile {
pub name: String,
pub content_type: String,
pub bytes: Vec<u8>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct PickOpenFilesResult {
pub files: Vec<PickedFile>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct PickOpenFilesError {
pub code: String,
pub message: String,
}
pub struct PickOpenFilesCapability;
impl OperationCapability for PickOpenFilesCapability {
type Request = PickOpenFilesRequest;
type Ok = PickOpenFilesResult;
type Err = PickOpenFilesError;
}
pub const PICK_OPEN_FILES: CapabilityType<PickOpenFilesCapability> =
CapabilityType::new("fission.fs.pick_open");
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pick_open_files_round_trips() {
let request = PickOpenFilesRequest {
allow_multiple: true,
mime_types: vec!["image/png".into(), "application/pdf".into()],
extensions: vec!["png".into(), "pdf".into()],
};
let bytes = serde_json::to_vec(&request).unwrap();
let decoded: PickOpenFilesRequest = serde_json::from_slice(&bytes).unwrap();
assert_eq!(decoded, request);
let result = PickOpenFilesResult {
files: vec![PickedFile {
name: "receipt.pdf".into(),
content_type: "application/pdf".into(),
bytes: b"hello".to_vec(),
}],
};
let bytes = serde_json::to_vec(&result).unwrap();
let decoded: PickOpenFilesResult = serde_json::from_slice(&bytes).unwrap();
assert_eq!(decoded, result);
}
#[test]
fn open_url_round_trips() {
let request = OpenUrlRequest {
url: "https://fission.dev".into(),
in_app: false,
};
let bytes = serde_json::to_vec(&request).unwrap();
let decoded: OpenUrlRequest = serde_json::from_slice(&bytes).unwrap();
assert_eq!(decoded, request);
}
}