diff --git a/radicle/src/lib.rs b/radicle/src/lib.rs index 592dcd18..c2be087c 100644 --- a/radicle/src/lib.rs +++ b/radicle/src/lib.rs @@ -24,7 +24,7 @@ pub mod node; pub mod profile; pub mod rad; #[cfg(feature = "schemars")] -pub(crate) mod schemars_ext; +pub mod schemars_ext; pub mod serde_ext; pub mod sql; pub mod storage; diff --git a/radicle/src/node.rs b/radicle/src/node.rs index 2037d452..62ebc83c 100644 --- a/radicle/src/node.rs +++ b/radicle/src/node.rs @@ -96,6 +96,7 @@ pub enum PingState { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[allow(clippy::large_enum_variant)] #[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum State { /// Initial state for outgoing connections. Initial, @@ -106,6 +107,10 @@ pub enum State { Connected { /// Connected since this time. #[serde(with = "crate::serde_ext::localtime::time")] + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::localtime::LocalDurationInSeconds") + )] since: LocalTime, /// Ping state. #[serde(skip)] @@ -124,9 +129,17 @@ pub enum State { Disconnected { /// Since when has this peer been disconnected. #[serde(with = "crate::serde_ext::localtime::time")] + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::localtime::LocalDurationInSeconds") + )] since: LocalTime, /// When to retry the connection. #[serde(with = "crate::serde_ext::localtime::time")] + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::localtime::LocalDurationInSeconds") + )] retry_at: LocalTime, }, } @@ -185,6 +198,7 @@ impl Penalty { #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] #[serde(tag = "status")] #[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum SyncStatus { /// We're in sync. #[serde(rename_all = "camelCase")] @@ -428,6 +442,7 @@ impl TryFrom<&sqlite::Value> for Alias { /// Options passed to the "connect" node command. #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ConnectOptions { /// Establish a persistent connection. pub persistent: bool, @@ -480,6 +495,7 @@ impl From for CommandResult { /// A success response. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Success { /// Whether something was updated. #[serde(default, skip_serializing_if = "crate::serde_ext::is_default")] @@ -589,6 +605,7 @@ impl From
for HostName { /// Command name. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase", tag = "command")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum Command { /// Announce repository references for given repository to peers. #[serde(rename_all = "camelCase")] @@ -616,7 +633,13 @@ pub enum Command { /// Disconnect from a node. #[serde(rename_all = "camelCase")] - Disconnect { nid: NodeId }, + Disconnect { + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::crypto::PublicKey") + )] + nid: NodeId, + }, /// Lookup seeds for the given repository in the routing table. #[serde(rename_all = "camelCase")] @@ -626,12 +649,22 @@ pub enum Command { Sessions, /// Get a specific peer session. - Session { nid: NodeId }, + Session { + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::crypto::PublicKey") + )] + nid: NodeId, + }, /// Fetch the given repository from the network. #[serde(rename_all = "camelCase")] Fetch { rid: RepoId, + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::crypto::PublicKey") + )] nid: NodeId, timeout: time::Duration, }, @@ -646,11 +679,24 @@ pub enum Command { /// Follow the given node. #[serde(rename_all = "camelCase")] - Follow { nid: NodeId, alias: Option }, + Follow { + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::crypto::PublicKey") + )] + nid: NodeId, + alias: Option, + }, /// Unfollow the given node. #[serde(rename_all = "camelCase")] - Unfollow { nid: NodeId }, + Unfollow { + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::crypto::PublicKey") + )] + nid: NodeId, + }, /// Get the node's status. Status, @@ -679,6 +725,7 @@ impl Command { /// Connection link direction. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum Link { /// Outgoing connection. Outbound, @@ -688,7 +735,12 @@ pub enum Link { /// An established network connection with a peer. #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Session { + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::crypto::PublicKey") + )] pub nid: NodeId, pub link: Link, pub addr: Address, @@ -705,8 +757,14 @@ impl Session { /// A seed for some repository, with metadata about its status. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] + pub struct Seed { /// The Node ID. + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::crypto::PublicKey") + )] pub nid: NodeId, /// Known addresses for this seed. pub addrs: Vec, @@ -748,7 +806,11 @@ impl Seed { /// Represents a set of seeds with associated metadata. Uses an RNG /// underneath, so every iteration returns a different ordering. #[serde(into = "Vec", from = "Vec")] -pub struct Seeds(address::AddressBook); +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct Seeds( + #[cfg_attr(feature = "schemars", schemars(with = "Vec"))] + address::AddressBook, +); impl Seeds { /// Create a new seeds list from an RNG. @@ -819,9 +881,14 @@ impl From> for Seeds { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "status", rename_all = "camelCase")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum FetchResult { Success { updated: Vec, + #[cfg_attr( + feature = "schemars", + schemars(with = "HashSet") + )] namespaces: HashSet, clone: bool, }, @@ -978,6 +1045,7 @@ impl Error { #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", tag = "status")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum ConnectResult { Connected, Disconnected { reason: String }, diff --git a/radicle/src/node/address.rs b/radicle/src/node/address.rs index dac9ac43..52a087ec 100644 --- a/radicle/src/node/address.rs +++ b/radicle/src/node/address.rs @@ -143,6 +143,7 @@ pub struct Node { /// A known address. #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct KnownAddress { /// Network address. pub addr: Address, @@ -150,9 +151,17 @@ pub struct KnownAddress { pub source: Source, /// Last time this address was used to successfully connect to a peer. #[serde(with = "crate::serde_ext::localtime::option::time")] + #[cfg_attr( + feature = "schemars", + schemars(with = "Option") + )] pub last_success: Option, /// Last time this address was tried. #[serde(with = "crate::serde_ext::localtime::option::time")] + #[cfg_attr( + feature = "schemars", + schemars(with = "Option") + )] pub last_attempt: Option, /// Whether this address has been banned. pub banned: bool, @@ -174,6 +183,7 @@ impl KnownAddress { /// Address source. Specifies where an address originated from. #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum Source { /// An address that was shared by another peer. Peer, diff --git a/radicle/src/node/seed.rs b/radicle/src/node/seed.rs index 3408e196..5e2ae4bf 100644 --- a/radicle/src/node/seed.rs +++ b/radicle/src/node/seed.rs @@ -11,11 +11,17 @@ use crate::storage::{refs::RefsAt, ReadRepository, RemoteId}; /// Holds an oid and timestamp. #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct SyncedAt { /// Head of `rad/sigrefs`. + #[cfg_attr(feature = "schemars", schemars(with = "crate::schemars_ext::git::Oid"))] pub oid: git_ext::Oid, /// When these refs were synced. #[serde(with = "crate::serde_ext::localtime::time")] + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::localtime::LocalDurationInSeconds") + )] pub timestamp: LocalTime, } diff --git a/radicle/src/schemars_ext.rs b/radicle/src/schemars_ext.rs index 34cc5edb..9157c729 100644 --- a/radicle/src/schemars_ext.rs +++ b/radicle/src/schemars_ext.rs @@ -4,6 +4,29 @@ use schemars::JsonSchema; +pub mod crypto { + use super::*; + /// See [`crate::node::NodeId`] + /// See [`crate::storage::RemoteId`] + /// See [`::crypto::PublicKey`] + /// + /// An Ed25519 public key in multibase encoding. + /// + /// `MULTIBASE(base58-btc, MULTICODEC(public-key-type, raw-public-key-bytes))` + #[derive(JsonSchema)] + #[schemars( + title = "NodeId", + description = "An Ed25519 public key in multibase encoding.", + extend("examples" = [ + "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7", + "z6MkvUJtYD9dHDJfpevWRT98mzDDpdAtmUjwyDSkyqksUr7C", + "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi", + "z6MkkfM3tPXNPrPevKr3uSiQtHPuwnNhu2yUVjgd2jXVsVz5", + ]), +)] + pub struct PublicKey(String); +} + pub(crate) mod log { use super::*; @@ -56,4 +79,34 @@ pub(crate) mod localtime { description = "A time duration measured locally in milliseconds." )] pub(crate) struct LocalDuration(u64); + + /// See [`crate::serde_ext::localtime::time`] + #[derive(JsonSchema)] + #[schemars( + remote = "localtime::LocalDuration", + description = "A time duration measured locally in seconds." + )] + pub(crate) struct LocalDurationInSeconds(u64); +} + +pub(crate) mod git { + use super::*; + + /// See [`crate::git::Oid`] + /// See [`::git_ext::Oid`] + /// See [`::git2::Oid`] + /// + /// A Git Object Identifier in hexadecimal encoding. + #[derive(JsonSchema)] + #[schemars( + remote = "git2::Oid", + description = "A Git Object Identifier (SHA-1 or SHA-256 hash) in hexadecimal encoding." + )] + pub(crate) struct Oid( + #[schemars(regex(pattern = r"^([0-9a-fA-F]{64}|[0-9a-fA-F]{40})$"))] String, + ); + + /// See [`crate::git::RefString`] + #[derive(JsonSchema)] + pub(crate) struct RefString(String); } diff --git a/radicle/src/storage.rs b/radicle/src/storage.rs index 4b454b1e..b4113639 100644 --- a/radicle/src/storage.rs +++ b/radicle/src/storage.rs @@ -191,11 +191,43 @@ pub type RemoteId = PublicKey; /// An update to a reference. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum RefUpdate { - Updated { name: RefString, old: Oid, new: Oid }, - Created { name: RefString, oid: Oid }, - Deleted { name: RefString, oid: Oid }, - Skipped { name: RefString, oid: Oid }, + Updated { + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::git::RefString") + )] + name: RefString, + #[cfg_attr(feature = "schemars", schemars(with = "crate::schemars_ext::git::Oid"))] + old: Oid, + #[cfg_attr(feature = "schemars", schemars(with = "crate::schemars_ext::git::Oid"))] + new: Oid, + }, + Created { + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::git::RefString") + )] + name: RefString, + #[cfg_attr(feature = "schemars", schemars(with = "crate::schemars_ext::git::Oid"))] + oid: Oid, + }, + Deleted { + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::git::RefString") + )] + name: RefString, + #[cfg_attr(feature = "schemars", schemars(with = "crate::schemars_ext::git::Oid"))] + oid: Oid, + }, + Skipped { + #[cfg_attr(feature = "schemars", schemars(with = "String"))] + name: RefString, + #[cfg_attr(feature = "schemars", schemars(with = "crate::schemars_ext::git::Oid"))] + oid: Oid, + }, } impl RefUpdate { diff --git a/radicle/src/storage/refs.rs b/radicle/src/storage/refs.rs index 3d24e1f6..a14c93d5 100644 --- a/radicle/src/storage/refs.rs +++ b/radicle/src/storage/refs.rs @@ -377,10 +377,16 @@ impl Deref for SignedRefs { /// references to other nodes. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct RefsAt { /// The remote namespace of the `rad/sigrefs`. + #[cfg_attr( + feature = "schemars", + schemars(with = "crate::schemars_ext::crypto::PublicKey") + )] pub remote: RemoteId, /// The commit SHA that `rad/sigrefs` points to. + #[cfg_attr(feature = "schemars", schemars(with = "crate::schemars_ext::git::Oid"))] pub at: Oid, }