From ec78fb9ff2972b7cec3248991b9dac3117637fe5 Mon Sep 17 00:00:00 2001
From: Federico Terzi <federicoterzi96@gmail.com>
Date: Sat, 20 Mar 2021 10:33:38 +0100
Subject: [PATCH] feat(render): implement clipboard extension

---
 espanso-render/src/extension/clipboard.rs | 100 ++++++++++++++++++++++
 espanso-render/src/extension/mod.rs       |   3 +-
 2 files changed, 102 insertions(+), 1 deletion(-)
 create mode 100644 espanso-render/src/extension/clipboard.rs

diff --git a/espanso-render/src/extension/clipboard.rs b/espanso-render/src/extension/clipboard.rs
new file mode 100644
index 0000000..e42743e
--- /dev/null
+++ b/espanso-render/src/extension/clipboard.rs
@@ -0,0 +1,100 @@
+/*
+ * This file is part of espanso.
+ *
+ * Copyright (C) 2019-2021 Federico Terzi
+ *
+ * espanso is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * espanso is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use crate::{Extension, ExtensionOutput, ExtensionResult, Params};
+use thiserror::Error;
+
+pub trait ClipboardProvider {
+  fn get_text(&self) -> Option<String>;
+}
+
+pub struct ClipboardExtension {
+  provider: Box<dyn ClipboardProvider>,
+}
+
+#[allow(clippy::new_without_default)]
+impl ClipboardExtension {
+  pub fn new(provider: Box<dyn ClipboardProvider>) -> Self {
+    Self { provider }
+  }
+}
+
+impl Extension for ClipboardExtension {
+  fn name(&self) -> &str {
+    "clipboard"
+  }
+
+  fn calculate(&self, _: &crate::Context, _: &crate::Scope, _: &Params) -> crate::ExtensionResult {
+    if let Some(clipboard) = self.provider.get_text() {
+      ExtensionResult::Success(ExtensionOutput::Single(clipboard))
+    } else {
+      ExtensionResult::Error(ClipboardExtensionError::MissingClipboard.into())
+    }
+  }
+}
+
+#[derive(Error, Debug)]
+pub enum ClipboardExtensionError {
+  #[error("clipboard provider returned error")]
+  MissingClipboard,
+}
+
+#[cfg(test)]
+mod tests {
+  use super::*;
+
+  struct MockClipboardProvider {
+    return_none: bool,
+  }
+
+  impl super::ClipboardProvider for MockClipboardProvider {
+    fn get_text(&self) -> Option<String> {
+      if self.return_none {
+        None
+      } else {
+        Some("test".to_string())
+      }
+    }
+  }
+
+  #[test]
+  fn clipboard_works_correctly() {
+    let provider = MockClipboardProvider { return_none: false };
+    let extension = ClipboardExtension::new(Box::new(provider));
+
+    assert_eq!(
+      extension
+        .calculate(&Default::default(), &Default::default(), &Params::new())
+        .into_success()
+        .unwrap(),
+      ExtensionOutput::Single("test".to_string())
+    );
+  }
+
+  #[test]
+  fn none_clipboard_produces_error() {
+    let provider = MockClipboardProvider { return_none: true };
+    let extension = ClipboardExtension::new(Box::new(provider));
+
+    assert!(matches!(
+      extension.calculate(&Default::default(), &Default::default(), &Params::new()),
+      ExtensionResult::Error(_)
+    ));
+  }
+}
diff --git a/espanso-render/src/extension/mod.rs b/espanso-render/src/extension/mod.rs
index d3dbe53..31006bb 100644
--- a/espanso-render/src/extension/mod.rs
+++ b/espanso-render/src/extension/mod.rs
@@ -18,4 +18,5 @@
  */
 
 pub mod date;
-pub mod echo;
\ No newline at end of file
+pub mod echo;
+pub mod clipboard;
\ No newline at end of file