Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
name = "kinode_process_lib"
description = "A library for writing Kinode processes in Rust."
version = "0.5.9"
version = "0.6.0"
edition = "2021"
license-file = "LICENSE"
homepage = "https://kinode.org"
repository = "https://github.com/kinode-dao/process_lib"

[features]
eth = [ "ethers-core", "alloy-rpc-types" ]
eth = ["ethers-core", "alloy-rpc-types"]

[dependencies]
alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy.git", rev = "3b1c310", optional = true }
Expand Down
194 changes: 99 additions & 95 deletions src/capability.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub use crate::{Address, Capability, ProcessId};
use serde::{Deserialize, Serialize};
pub use crate::{Address, Capability};
use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
use serde::ser::{Serialize, SerializeStruct};
use std::hash::{Hash, Hasher};

/// Capability is defined in the wit bindings, but constructors and methods here.
Expand Down Expand Up @@ -29,66 +30,15 @@ impl Capability {
}
}

impl std::str::FromStr for Capability {
type Err = CapabilityParseError;
/// Attempt to parse a `Capability` from a string. The formatting structure for
/// a Capability is `issuer^params`.
/// TODO not tested
fn from_str(input: &str) -> Result<Self, CapabilityParseError> {
// split string on colons into 4 segments,
// first one with @, next 3 with :
let mut name_rest = input.split('@');
let node = name_rest
.next()
.ok_or(CapabilityParseError::MissingField)?
.to_string();
let mut param_segments = name_rest
.next()
.ok_or(CapabilityParseError::MissingNodeId)?
.split('^');
let mut segments = param_segments
.next()
.ok_or(CapabilityParseError::MissingNodeId)?
.split(':');
let process_name = segments
.next()
.ok_or(CapabilityParseError::MissingField)?
.to_string();
let package_name = segments
.next()
.ok_or(CapabilityParseError::MissingField)?
.to_string();
let publisher_node = segments
.next()
.ok_or(CapabilityParseError::MissingField)?
.to_string();
let params = param_segments
.next()
.ok_or(CapabilityParseError::MissingParams)?
.to_string();
if segments.next().is_some() {
return Err(CapabilityParseError::TooManyColons);
}
Ok(Capability {
issuer: Address {
node,
process: ProcessId {
process_name,
package_name,
publisher_node,
},
},
params,
})
}
}

impl Serialize for Capability {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
format!("{}", self).serialize(serializer)
let mut state = serializer.serialize_struct("Capability", 2)?;
state.serialize_field("issuer", &self.issuer)?;
state.serialize_field("params", &self.params)?;
state.end()
}
}

Expand All @@ -97,8 +47,97 @@ impl<'a> Deserialize<'a> for Capability {
where
D: serde::de::Deserializer<'a>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
enum Field {
Issuer,
Params,
}

impl<'de> Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
where
D: Deserializer<'de>,
{
struct FieldVisitor;

impl<'de> Visitor<'de> for FieldVisitor {
type Value = Field;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("`issuer` or `params`")
}

fn visit_str<E>(self, value: &str) -> Result<Field, E>
where
E: de::Error,
{
match value {
"issuer" => Ok(Field::Issuer),
"params" => Ok(Field::Params),
_ => Err(de::Error::unknown_field(value, FIELDS)),
}
}
}

deserializer.deserialize_identifier(FieldVisitor)
}
}

struct CapabilityVisitor;

impl<'de> Visitor<'de> for CapabilityVisitor {
type Value = Capability;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("struct Capability")
}

fn visit_seq<V>(self, mut seq: V) -> Result<Capability, V::Error>
where
V: SeqAccess<'de>,
{
let issuer: Address = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let params: String = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
Ok(Capability::new(issuer, params))
}

fn visit_map<V>(self, mut map: V) -> Result<Capability, V::Error>
where
V: MapAccess<'de>,
{
let mut issuer: Option<Address> = None;
let mut params: Option<String> = None;
while let Some(key) = map.next_key()? {
match key {
Field::Issuer => {
if issuer.is_some() {
return Err(de::Error::duplicate_field("issuer"));
}
issuer = Some(map.next_value()?);
}
Field::Params => {
if params.is_some() {
return Err(de::Error::duplicate_field("params"));
}
params = Some(map.next_value()?);
}
}
}
let issuer: Address = issuer
.ok_or_else(|| de::Error::missing_field("issuer"))?
.into();
let params: String = params
.ok_or_else(|| de::Error::missing_field("params"))?
.into();
Ok(Capability::new(issuer, params))
}
}

const FIELDS: &'static [&'static str] = &["issuer", "params"];
deserializer.deserialize_struct("Capability", FIELDS, CapabilityVisitor)
}
}

Expand Down Expand Up @@ -134,41 +173,6 @@ where

impl std::fmt::Display for Capability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}^{}", self.issuer, self.params)
}
}

/// Error type for parsing an `Address` from a string.
#[derive(Debug)]
pub enum CapabilityParseError {
TooManyColons,
MissingNodeId,
MissingField,
MissingParams,
}

impl std::fmt::Display for CapabilityParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
CapabilityParseError::TooManyColons => "Too many colons in ProcessId string",
CapabilityParseError::MissingNodeId => "Node ID missing",
CapabilityParseError::MissingField => "Missing field in ProcessId string",
CapabilityParseError::MissingParams => "Missing params in Capability string",
}
)
}
}

impl std::error::Error for CapabilityParseError {
fn description(&self) -> &str {
match self {
CapabilityParseError::TooManyColons => "Too many colons in ProcessId string",
CapabilityParseError::MissingNodeId => "Node ID missing",
CapabilityParseError::MissingField => "Missing field in ProcessId string",
CapabilityParseError::MissingParams => "Missing params in Capability string",
}
write!(f, "{}({})", self.issuer, self.params)
}
}
7 changes: 6 additions & 1 deletion src/kernel_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ pub enum KernelCommand {
target: ProcessId,
capabilities: Vec<Capability>,
},
/// Drop capabilities. Does nothing if process doesn't have these caps
DropCapabilities {
target: ProcessId,
capabilities: Vec<Capability>,
},
/// Tell the kernel to run a process that has already been installed.
/// TODO: in the future, this command could be extended to allow for
/// resource provision.
Expand Down Expand Up @@ -219,7 +224,7 @@ pub struct PackageManifestEntry {
}

/// the type that gets deserialized from a `scripts.json` object
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DotScriptsEntry {
pub root: bool,
pub public: bool,
Expand Down