Skip to main content

diffr_plugin_sdk/
host.rs

1//! What diffr gives every plugin: `git`, the `host` interface of
2//! `wit/plugin.wit`. A plugin calls it the same way wherever it runs. In a
3//! component it calls the import. Natively, diffr runs each call of a plugin
4//! inside [`scope`], with its own implementation of [`Host`], and the
5//! function calls that.
6//!
7//! Everything else a plugin needs it does itself: it reads files, and writes
8//! to stderr, which diffr captures and writes to its own.
9
10/// Run `git` with `args` in the repository's working directory: its stdout
11/// when it exits successfully, its stderr otherwise.
12pub fn git(args: &[String]) -> Result<String, String> {
13    imp::git(args)
14}
15
16#[cfg(target_arch = "wasm32")]
17mod imp {
18    use crate::bindings::diffr::plugin::host;
19
20    pub(super) fn git(args: &[String]) -> Result<String, String> {
21        host::git(args)
22    }
23}
24
25/// diffr's implementation of the host functions for one call of a native
26/// plugin.
27#[cfg(not(target_arch = "wasm32"))]
28pub trait Host {
29    fn git(&self, args: &[String]) -> Result<String, String>;
30}
31
32/// Run `call`, one call of a native plugin, with `host` behind the host
33/// function on this thread.
34#[cfg(not(target_arch = "wasm32"))]
35pub fn scope<R>(host: std::rc::Rc<dyn Host>, call: impl FnOnce() -> R) -> R {
36    imp::scope(host, call)
37}
38
39#[cfg(not(target_arch = "wasm32"))]
40mod imp {
41    use super::Host;
42    use std::cell::RefCell;
43    use std::rc::Rc;
44
45    thread_local! {
46        static CURRENT: RefCell<Option<Rc<dyn Host>>> = const { RefCell::new(None) };
47    }
48
49    /// Restores the host of the enclosing scope, if any, when a scope ends.
50    struct Restore(Option<Rc<dyn Host>>);
51
52    impl Drop for Restore {
53        fn drop(&mut self) {
54            CURRENT.with(|current| *current.borrow_mut() = self.0.take());
55        }
56    }
57
58    pub(super) fn scope<R>(host: Rc<dyn Host>, call: impl FnOnce() -> R) -> R {
59        let _restore = Restore(CURRENT.with(|current| current.borrow_mut().replace(host)));
60        call()
61    }
62
63    fn current() -> Rc<dyn Host> {
64        CURRENT.with(|current| {
65            current
66                .borrow()
67                .clone()
68                .expect("the host function is called only while diffr runs a plugin")
69        })
70    }
71
72    pub(super) fn git(args: &[String]) -> Result<String, String> {
73        current().git(args)
74    }
75}