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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
//! Kinode process standard library for Rust compiled to Wasm
//! Must be used in context of bindings generated by `kinode.wit`.
//!
//! This library provides a set of functions for interacting with the kinode
//! kernel interface, which is a WIT file. The types generated by this file
//! are available in processes via the wit_bindgen macro, if a process needs
//! to use them directly. However, the most convenient way to do most things
//! will be via this library.
//!
//! We define wrappers over the wit bindings to make them easier to use.
//! This library encourages the use of IPC body and metadata types serialized and
//! deserialized to JSON, which is not optimal for performance, but useful
//! for applications that want to maximize composability and introspectability.
//! For blobs, we recommend bincode to serialize and deserialize to bytes.
//!
pub use crate::kinode::process::standard::*;
use serde_json::Value;

wit_bindgen::generate!({
    path: "kinode-wit",
    generate_unused_types: true,
    world: "lib",
});

/// Interact with the eth provider module.
pub mod eth;
/// Interact with the system homepage.
///
/// Your process must have the capability to message
/// `homepage:homepage:sys` to use this module.
pub mod homepage;
/// Interact with the HTTP server and client modules.
/// Contains types from the `http` crate to use as well.
///
/// Your process must have the capability to message and receive messages from
/// `http_server:distro:sys` and/or `http_client:distro:sys` to use this module.
pub mod http;
/// The types that the kernel itself uses -- warning -- these will
/// be incompatible with WIT types in some cases, leading to annoying errors.
/// Use only to interact with the kernel or runtime in certain ways.
pub mod kernel_types;
/// Interact with kimap, the onchain namespace
pub mod kimap;
/// Interact with the key_value module
///
/// Your process must have the capability to message and receive messages from
/// `kv:distro:sys` to use this module.
pub mod kv;
#[cfg(feature = "logging")]
pub mod logging;
/// Interact with the networking module
/// For configuration, debugging, and creating signatures with networking key.
///
/// Your process must have the capability to message and receive messages from
/// `net:distro:sys` to use this module.
pub mod net;
/// Interact with the sqlite module
///
/// Your process must have the capability to message and receive messages from
/// `sqlite:distro:sys` to use this module.
pub mod sqlite;
/// Interact with the timer runtime module.
///
/// The `timer:distro:sys` module is public, so no special capabilities needed.
pub mod timer;
/// Interact with the virtual filesystem
///
/// Your process must have the capability to message and receive messages from
/// `vfs:distro:sys` to use this module.
pub mod vfs;

/// A set of types and macros for writing "script" processes.
pub mod scripting;

mod types;
pub use types::{
    address::{Address, AddressParseError},
    capability::Capability,
    lazy_load_blob::LazyLoadBlob,
    message::{Message, _wit_message_to_message},
    on_exit::OnExit,
    package_id::PackageId,
    process_id::{ProcessId, ProcessIdParseError},
    request::Request,
    response::Response,
    send_error::{SendError, SendErrorKind, _wit_send_error_to_send_error},
};

/// Implement the wit-bindgen specific code that the kernel uses to hook into
/// a process. Write an `init(our: Address)` function and call it with this.
#[macro_export]
macro_rules! call_init {
    ($init_func:ident) => {
        struct Component;
        impl Guest for Component {
            fn init(our: String) {
                let our: Address = our.parse().unwrap();
                $init_func(our);
            }
        }
        export!(Component);
    };
}

/// Override the println! macro to print to the terminal. Uses the
/// `print_to_terminal` function from the WIT interface on maximally-verbose
/// mode, i.e., this print will always show up in the terminal. To control
/// the verbosity, use the `print_to_terminal` function directly.
#[macro_export]
macro_rules! println {
    () => {
        $crate::print_to_terminal(0, "\n");
    };
    ($($arg:tt)*) => {{
        $crate::print_to_terminal(0, &format!($($arg)*));
    }};
}

/// Uses the `print_to_terminal` function from the WIT interface on maximally-verbose
/// mode, i.e., this print will always show up in the terminal. To control
/// the verbosity, use the `print_to_terminal` function directly.
#[macro_export]
macro_rules! kiprintln {
    () => {
        $crate::print_to_terminal(0, "\n");
    };
    ($($arg:tt)*) => {{
        $crate::print_to_terminal(0, &format!($($arg)*));
    }};
}

/// Await the next message sent to this process. The runtime will handle the
/// queueing of incoming messages, and calling this function will provide the next one.
/// Interwoven with incoming messages are errors from the network. If your process
/// attempts to send a message to another node, that message may bounce back with
/// a `SendError`. Those should be handled here.
///
/// Example:
/// ```no_run
/// use kinode_process_lib::{await_message, println};
///
/// loop {
///     match await_message() {
///         Ok(msg) => {
///             println!("Received message: {:?}", msg);
///             // Do something with the message
///         }
///         Err(send_error) => {
///             println!("Error sending message: {:?}", send_error);
///         }
///     }
/// }
/// ```
pub fn await_message() -> Result<Message, SendError> {
    match crate::receive() {
        Ok((source, message)) => Ok(_wit_message_to_message(source, message)),
        Err((send_err, context)) => Err(_wit_send_error_to_send_error(send_err, context)),
    }
}

/// Get the next message body from the message queue, or propagate the error.
pub fn await_next_message_body() -> Result<Vec<u8>, SendError> {
    match await_message() {
        Ok(msg) => Ok(msg.body().to_vec()),
        Err(e) => Err(e.into()),
    }
}

/// Spawn a new process. This function is a wrapper around the standard `spawn()` function
/// provided in `kinode::process::standard` (which is generated by the WIT file).
pub fn spawn(
    name: Option<&str>,
    wasm_path: &str,
    on_exit: OnExit,
    request_capabilities: Vec<Capability>,
    grant_capabilities: Vec<ProcessId>,
    public: bool,
) -> Result<ProcessId, SpawnError> {
    crate::kinode::process::standard::spawn(
        name,
        wasm_path,
        &on_exit._to_standard().map_err(|_e| SpawnError::NameTaken)?,
        &request_capabilities,
        &grant_capabilities,
        public,
    )
}

/// Create a blob with no MIME type and a generic type, plus a serializer
/// function that turns that type into bytes.
///
/// Example usage:
/// ```no_run
/// use kinode_process_lib::make_blob;
/// use bincode;
/// use serde::{Serialize, Deserialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct MyType {
///    field: std::collections::HashMap<String, String>,
///    field_two: std::collections::HashSet<String>,
/// }
///
/// let my_type = MyType {
///    field: std::collections::HashMap::new(),
///    field_two: std::collections::HashSet::new(),
/// };
///
/// make_blob(&my_type, |t| Ok(bincode::serialize(t)?));
/// ```
pub fn make_blob<T, F, E>(blob: &T, serializer: F) -> Result<LazyLoadBlob, E>
where
    F: Fn(&T) -> Result<Vec<u8>, E>,
    E: std::error::Error,
{
    Ok(LazyLoadBlob {
        mime: None,
        bytes: serializer(blob)?,
    })
}

/// Fetch the blob of the most recent message we've received. Returns `None`
/// if that message had no blob. If it does have one, attempt to deserialize
/// it from bytes with the provided function.
///
/// Example:
/// ```no_run
/// use std::collections::{HashMap, HashSet};
/// use kinode_process_lib::get_typed_blob;
/// use bincode;
/// use serde::{Serialize, Deserialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct MyType {
///   field: HashMap<String, String>,
///   field_two: HashSet<String>,
/// }
///
/// get_typed_blob(|bytes| Ok(bincode::deserialize(bytes)?)).unwrap_or(MyType {
///     field: HashMap::new(),
///     field_two: HashSet::new(),
/// });
/// ```
pub fn get_typed_blob<T, F, E>(deserializer: F) -> Option<T>
where
    F: Fn(&[u8]) -> Result<T, E>,
    E: std::error::Error,
{
    match crate::get_blob() {
        Some(blob) => match deserializer(&blob.bytes) {
            Ok(thing) => Some(thing),
            Err(_) => None,
        },
        None => None,
    }
}

/// Fetch the persisted state blob associated with this process. This blob is saved
/// using the [`set_state`] function. Returns `None` if this process has no saved state.
/// If it does, attempt to deserialize it from bytes with the provided function.
///
/// Example:
/// ```no_run
/// use std::collections::{HashMap, HashSet};
/// use kinode_process_lib::get_typed_state;
/// use bincode;
/// use serde::{Serialize, Deserialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct MyStateType {
///    field: HashMap<String, String>,
///    field_two: HashSet<String>,
/// }
///
/// get_typed_state(|bytes| Ok(bincode::deserialize(bytes)?)).unwrap_or(MyStateType {
///     field: HashMap::new(),
///     field_two: HashSet::new(),
/// });
/// ```
pub fn get_typed_state<T, F, E>(deserializer: F) -> Option<T>
where
    F: Fn(&[u8]) -> Result<T, E>,
    E: std::error::Error,
{
    match crate::get_state() {
        Some(bytes) => match deserializer(&bytes) {
            Ok(thing) => Some(thing),
            Err(_) => None,
        },
        None => None,
    }
}

/// See if we have the capability to message a certain process.
/// Note if you have not saved the capability, you will not be able to message the other process.
pub fn can_message(address: &Address) -> bool {
    crate::our_capabilities()
        .iter()
        .any(|cap| cap.params == "\"messaging\"" && cap.issuer == *address)
}

/// Get a capability in our store
pub fn get_capability(issuer: &Address, params: &str) -> Option<Capability> {
    let params = serde_json::from_str::<Value>(params).unwrap_or_default();
    crate::our_capabilities().into_iter().find(|cap| {
        let cap_params = serde_json::from_str::<Value>(&cap.params).unwrap_or_default();
        cap.issuer == *issuer && params == cap_params
    })
}