Skip to main content

fission_core/
platform_clipboard.rs

1//! Clipboard host capabilities.
2
3use crate::capability::{CapabilityType, OperationCapability};
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
7pub struct ClipboardText {
8    pub text: Option<String>,
9}
10
11#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
12pub struct ClipboardWriteTextRequest {
13    pub text: String,
14}
15
16#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
17pub struct ClipboardItem {
18    pub content_type: String,
19    pub bytes: Vec<u8>,
20    pub suggested_name: Option<String>,
21}
22
23#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
24pub struct ClipboardContent {
25    pub items: Vec<ClipboardItem>,
26}
27
28#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
29pub struct ClipboardError {
30    pub code: String,
31    pub message: String,
32}
33
34impl ClipboardError {
35    /// Creates a portable clipboard error payload.
36    ///
37    /// `code` should be a stable, machine-readable reason such as
38    /// `unsupported`, `permission_denied`, or `timeout`. `message` should be a
39    /// concise human-readable explanation suitable for logs or developer-facing
40    /// diagnostics.
41    pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
42        Self {
43            code: code.into(),
44            message: message.into(),
45        }
46    }
47
48    /// Creates the standard unsupported-operation error for this capability.
49    ///
50    /// `operation` should name the attempted clipboard operation. Use this
51    /// from hosts that implement the capability contract but cannot provide this
52    /// operation on the current platform or hardware.
53    pub fn unsupported(operation: impl Into<String>) -> Self {
54        Self::new(
55            "unsupported",
56            format!(
57                "clipboard operation `{}` is not supported by this host",
58                operation.into()
59            ),
60        )
61    }
62}
63
64pub struct ReadClipboardTextCapability;
65impl OperationCapability for ReadClipboardTextCapability {
66    type Request = ();
67    type Ok = ClipboardText;
68    type Err = ClipboardError;
69}
70
71pub struct WriteClipboardTextCapability;
72impl OperationCapability for WriteClipboardTextCapability {
73    type Request = ClipboardWriteTextRequest;
74    type Ok = ();
75    type Err = ClipboardError;
76}
77
78pub struct ReadClipboardContentCapability;
79impl OperationCapability for ReadClipboardContentCapability {
80    type Request = ();
81    type Ok = ClipboardContent;
82    type Err = ClipboardError;
83}
84
85pub struct WriteClipboardContentCapability;
86impl OperationCapability for WriteClipboardContentCapability {
87    type Request = ClipboardContent;
88    type Ok = ();
89    type Err = ClipboardError;
90}
91
92pub struct ClearClipboardCapability;
93impl OperationCapability for ClearClipboardCapability {
94    type Request = ();
95    type Ok = ();
96    type Err = ClipboardError;
97}
98
99pub const READ_CLIPBOARD_TEXT: CapabilityType<ReadClipboardTextCapability> =
100    CapabilityType::new("fission.clipboard.read_text");
101pub const WRITE_CLIPBOARD_TEXT: CapabilityType<WriteClipboardTextCapability> =
102    CapabilityType::new("fission.clipboard.write_text");
103pub const READ_CLIPBOARD_CONTENT: CapabilityType<ReadClipboardContentCapability> =
104    CapabilityType::new("fission.clipboard.read_content");
105pub const WRITE_CLIPBOARD_CONTENT: CapabilityType<WriteClipboardContentCapability> =
106    CapabilityType::new("fission.clipboard.write_content");
107pub const CLEAR_CLIPBOARD: CapabilityType<ClearClipboardCapability> =
108    CapabilityType::new("fission.clipboard.clear");
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn clipboard_text_round_trips() {
116        let request = ClipboardWriteTextRequest {
117            text: "copy me".into(),
118        };
119        let bytes = serde_json::to_vec(&request).unwrap();
120        let decoded: ClipboardWriteTextRequest = serde_json::from_slice(&bytes).unwrap();
121        assert_eq!(decoded, request);
122    }
123
124    #[test]
125    fn clipboard_content_round_trips() {
126        let content = ClipboardContent {
127            items: vec![ClipboardItem {
128                content_type: "text/plain".into(),
129                bytes: b"copy me".to_vec(),
130                suggested_name: None,
131            }],
132        };
133        let bytes = serde_json::to_vec(&content).unwrap();
134        let decoded: ClipboardContent = serde_json::from_slice(&bytes).unwrap();
135        assert_eq!(decoded, content);
136    }
137}