cli: `rad config schema` emits JSON Schema
Leverage `schemars` to generate a JSON Schema from our structs for configurations and those occurring within them. The output of `rad config schema` can be used by editors to provide a smoother editing experience for the configuration file. Discovery attributes (both keys and values) is greatly improved with the schema helping in the background. Refer to: - https://json-schema.org - https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings - https://schemastore.org
This commit is contained in:
parent
5a2f26eaa7
commit
b608a78806
File diff suppressed because it is too large
Load Diff
|
|
@ -46,11 +46,12 @@ tree-sitter-bash = { version = "0.23.3" }
|
|||
tree-sitter-go = { version = "0.23.4" }
|
||||
tree-sitter-md = { version = "0.3.2" }
|
||||
zeroize = { version = "1.1" }
|
||||
schemars = { version = "1.0.0-alpha.17" }
|
||||
|
||||
[dependencies.radicle]
|
||||
version = "0"
|
||||
path = "../radicle"
|
||||
features = ["logger"]
|
||||
features = ["logger", "schemars"]
|
||||
|
||||
[dependencies.radicle-cli-test]
|
||||
version = "0"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ Usage
|
|||
rad config init --alias <alias> [<option>...]
|
||||
rad config edit [<option>...]
|
||||
rad config get <key> [<option>...]
|
||||
rad config schema [<option>...]
|
||||
rad config set <key> <value> [<option>...]
|
||||
rad config unset <key> [<option>...]
|
||||
rad config push <key> <value> [<option>...]
|
||||
|
|
@ -43,6 +44,7 @@ enum Operation {
|
|||
#[default]
|
||||
Show,
|
||||
Get(String),
|
||||
Schema,
|
||||
Set(String, String),
|
||||
Push(String, String),
|
||||
Remove(String, String),
|
||||
|
|
@ -79,6 +81,7 @@ impl Args for Options {
|
|||
}
|
||||
Value(val) if op.is_none() => match val.to_string_lossy().as_ref() {
|
||||
"show" => op = Some(Operation::Show),
|
||||
"schema" => op = Some(Operation::Schema),
|
||||
"edit" => op = Some(Operation::Edit),
|
||||
"init" => op = Some(Operation::Init),
|
||||
"get" => {
|
||||
|
|
@ -140,6 +143,9 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
let profile = ctx.profile()?;
|
||||
term::json::to_pretty(&profile.config, path.as_path())?.print();
|
||||
}
|
||||
Operation::Schema => {
|
||||
term::json::to_pretty(&schemars::schema_for!(Config), path.as_path())?.print();
|
||||
}
|
||||
Operation::Get(key) => {
|
||||
let mut temp_config = RawConfig::from_file(&path)?;
|
||||
let key: ConfigPath = key.into();
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ sqlite = { version = "0.32.0", features = ["bundled"] }
|
|||
tempfile = { version = "3.3.0" }
|
||||
thiserror = { version = "1" }
|
||||
unicode-normalization = { version = "0.1" }
|
||||
schemars = { version = "1.0.0-alpha.17", optional = true }
|
||||
|
||||
[dependencies.chrono]
|
||||
version = "0.4.0"
|
||||
|
|
@ -75,6 +76,7 @@ emojis = { version = "0.6" }
|
|||
pretty_assertions = { version = "1.3.0" }
|
||||
qcheck-macros = { version = "1", default-features = false }
|
||||
qcheck = { version = "1", default-features = false }
|
||||
jsonschema = { version = "0.30" }
|
||||
|
||||
[dev-dependencies.radicle-crypto]
|
||||
path = "../radicle-crypto"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
/// CLI configuration.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
derive(schemars::JsonSchema),
|
||||
schemars(rename = "CliConfig")
|
||||
)]
|
||||
pub struct Config {
|
||||
/// Whether to show hints or not in the CLI.
|
||||
#[serde(default)]
|
||||
|
|
|
|||
|
|
@ -81,8 +81,9 @@ impl std::fmt::Display for ExplorerUrl {
|
|||
}
|
||||
|
||||
/// A public explorer, eg. `https://app.radicle.xyz`.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(transparent)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct Explorer(String);
|
||||
|
||||
impl Default for Explorer {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,17 @@ pub enum IdError {
|
|||
|
||||
/// A repository identifier.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct RepoId(git::Oid);
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct RepoId(
|
||||
#[cfg_attr(feature = "schemars", schemars(
|
||||
with = "String",
|
||||
description = "A repository identifier. Starts with \"rad:\", followed by a multibase Base58 encoded Git object identifier.",
|
||||
regex(pattern = r"rad:z[1-9a-km-zA-HJ-NP-Z]+"),
|
||||
length(min = 5),
|
||||
example = &"rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
|
||||
))]
|
||||
git::Oid,
|
||||
);
|
||||
|
||||
impl fmt::Display for RepoId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ pub mod logger;
|
|||
pub mod node;
|
||||
pub mod profile;
|
||||
pub mod rad;
|
||||
#[cfg(feature = "schemars")]
|
||||
pub(crate) mod schemars_ext;
|
||||
pub mod serde_ext;
|
||||
pub mod sql;
|
||||
pub mod storage;
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ impl Penalty {
|
|||
}
|
||||
|
||||
/// Repository sync status for our own refs.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "status")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum SyncStatus {
|
||||
|
|
@ -221,7 +221,7 @@ impl PartialOrd for SyncStatus {
|
|||
}
|
||||
|
||||
/// Node user agent.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
|
||||
pub struct UserAgent(String);
|
||||
|
||||
impl UserAgent {
|
||||
|
|
@ -291,10 +291,25 @@ impl AsRef<str> for UserAgent {
|
|||
}
|
||||
}
|
||||
|
||||
/// Node alias.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, serde::Serialize, serde::Deserialize)]
|
||||
/// Node alias, i.e. a short and memorable name for it.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
|
||||
#[serde(try_from = "String", into = "String")]
|
||||
pub struct Alias(String);
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct Alias(
|
||||
// To exclude control characters, one might be inclined to use the character
|
||||
// class `[[:cntrl:]]` which is understood by the `regex` crate.
|
||||
// However, the patterns in JSON schema must conform to ECMA-262, which does
|
||||
// not specify the character class.
|
||||
// Thus, we unfold its definition from <https://www.unicode.org/reports/tr18/#cntrl>,
|
||||
// which refers to the "general category" named "Cc",
|
||||
// see <https://unicode.org/reports/tr44/#General_Category_Values>.
|
||||
// We obtain the two ranges below from <https://www.unicode.org/notes/tn36/Categories.txt>.
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
schemars(regex(pattern = r"^[^\x00-\x1F\x7F-\x9F\s]{0,32}$"), length(max = 32))
|
||||
)]
|
||||
String,
|
||||
);
|
||||
|
||||
impl Alias {
|
||||
/// Create a new alias from a string. Panics if the string is not a valid alias.
|
||||
|
|
@ -411,7 +426,7 @@ impl TryFrom<&sqlite::Value> for Alias {
|
|||
}
|
||||
|
||||
/// Options passed to the "connect" node command.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConnectOptions {
|
||||
/// Establish a persistent connection.
|
||||
pub persistent: bool,
|
||||
|
|
@ -500,10 +515,30 @@ impl<T: Serialize> CommandResult<T> {
|
|||
}
|
||||
|
||||
/// Peer public protocol address.
|
||||
#[derive(Wrapper, WrapperMut, Clone, Eq, PartialEq, Debug, Hash, From, Serialize, Deserialize)]
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Hash, From, Wrapper, WrapperMut, Serialize, Deserialize)]
|
||||
#[wrapper(Deref, Display, FromStr)]
|
||||
#[wrapper_mut(DerefMut)]
|
||||
pub struct Address(#[serde(with = "crate::serde_ext::string")] NetAddr<HostName>);
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
derive(schemars::JsonSchema),
|
||||
schemars(description = "\
|
||||
An IP address, or a DNS name, or a Tor onion name, followed by the symbol ':', \
|
||||
followed by a TCP port number.\
|
||||
")
|
||||
)]
|
||||
pub struct Address(
|
||||
#[serde(with = "crate::serde_ext::string")]
|
||||
#[cfg_attr(feature = "schemars", schemars(
|
||||
with = "String",
|
||||
regex(pattern = r"^.+:((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4}))$"),
|
||||
extend("examples" = [
|
||||
"xmrhfasfg5suueegrnc4gsgyi2tyclcy5oz7f5drnrodmdtob6t2ioyd.onion:8776",
|
||||
"seed.example.com:8776",
|
||||
"192.0.2.0:31337",
|
||||
]),
|
||||
))]
|
||||
NetAddr<HostName>,
|
||||
);
|
||||
|
||||
impl Address {
|
||||
/// Check whether this address is from the local network.
|
||||
|
|
@ -667,7 +702,7 @@ impl Session {
|
|||
}
|
||||
|
||||
/// A seed for some repository, with metadata about its status.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Seed {
|
||||
/// The Node ID.
|
||||
|
|
@ -708,7 +743,7 @@ impl Seed {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
/// Represents a set of seeds with associated metadata. Uses an RNG
|
||||
/// underneath, so every iteration returns a different ordering.
|
||||
#[serde(into = "Vec<Seed>", from = "Vec<Seed>")]
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::{fmt, net};
|
|||
|
||||
use cyphernet::addr::PeerAddr;
|
||||
use localtime::LocalDuration;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json as json;
|
||||
|
||||
use crate::node;
|
||||
|
|
@ -57,8 +58,9 @@ pub mod seeds {
|
|||
}
|
||||
|
||||
/// Peer-to-peer network.
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum Network {
|
||||
#[default]
|
||||
Main,
|
||||
|
|
@ -94,16 +96,25 @@ impl Network {
|
|||
}
|
||||
|
||||
/// Configuration parameters defining attributes of minima and maxima.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct Limits {
|
||||
/// Number of routing table entries before we start pruning.
|
||||
pub routing_max_size: usize,
|
||||
/// How long to keep a routing table entry before being pruned.
|
||||
#[serde(with = "crate::serde_ext::localtime::duration")]
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
schemars(with = "crate::schemars_ext::localtime::LocalDuration")
|
||||
)]
|
||||
pub routing_max_age: LocalDuration,
|
||||
/// How long to keep a gossip message entry before pruning it.
|
||||
#[serde(with = "crate::serde_ext::localtime::duration")]
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
schemars(with = "crate::schemars_ext::localtime::LocalDuration")
|
||||
)]
|
||||
pub gossip_max_age: LocalDuration,
|
||||
/// Maximum number of concurrent fetches per peer connection.
|
||||
pub fetch_concurrency: usize,
|
||||
|
|
@ -138,10 +149,19 @@ impl Default for Limits {
|
|||
/// Limiter for byte streams.
|
||||
///
|
||||
/// Default: 500MiB
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(into = "String", try_from = "String")]
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
derive(schemars::JsonSchema),
|
||||
schemars(transparent)
|
||||
)]
|
||||
pub struct FetchPackSizeLimit {
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
schemars(with = "crate::schemars_ext::bytesize::ByteSize")
|
||||
)]
|
||||
limit: bytesize::ByteSize,
|
||||
}
|
||||
|
||||
|
|
@ -213,8 +233,9 @@ impl Default for FetchPackSizeLimit {
|
|||
}
|
||||
|
||||
/// Connection limits.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct ConnectionLimits {
|
||||
/// Max inbound connections.
|
||||
pub inbound: usize,
|
||||
|
|
@ -232,16 +253,18 @@ impl Default for ConnectionLimits {
|
|||
}
|
||||
|
||||
/// Rate limts for a single connection.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct RateLimit {
|
||||
pub fill_rate: f64,
|
||||
pub capacity: usize,
|
||||
}
|
||||
|
||||
/// Rate limits for inbound and outbound connections.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct RateLimits {
|
||||
pub inbound: RateLimit,
|
||||
pub outbound: RateLimit,
|
||||
|
|
@ -263,9 +286,30 @@ impl Default for RateLimits {
|
|||
}
|
||||
|
||||
/// Full address used to connect to a remote node.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash)]
|
||||
#[serde(transparent)]
|
||||
pub struct ConnectAddress(#[serde(with = "crate::serde_ext::string")] PeerAddr<NodeId, Address>);
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
derive(schemars::JsonSchema),
|
||||
schemars(description = "\
|
||||
A node address to connect to. Format: An Ed25519 public key in multibase encoding, \
|
||||
followed by the symbol '@', followed by an IP address, or a DNS name, or a Tor onion \
|
||||
name, followed by the symbol ':', followed by a TCP port number.\
|
||||
")
|
||||
)]
|
||||
pub struct ConnectAddress(
|
||||
#[serde(with = "crate::serde_ext::string")]
|
||||
#[schemars(
|
||||
with = "String",
|
||||
regex(pattern = r"^.+@.+:((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4}))$"),
|
||||
extend("examples" = [
|
||||
"z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776",
|
||||
"z6MkvUJtYD9dHDJfpevWRT98mzDDpdAtmUjwyDSkyqksUr7C@xmrhfasfg5suueegrnc4gsgyi2tyclcy5oz7f5drnrodmdtob6t2ioyd.onion:8776",
|
||||
"z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi@seed.example.com:8776",
|
||||
"z6MkkfM3tPXNPrPevKr3uSiQtHPuwnNhu2yUVjgd2jXVsVz5@192.0.2.0:31337",
|
||||
]),
|
||||
)]
|
||||
PeerAddr<NodeId, Address>,
|
||||
);
|
||||
|
||||
impl From<PeerAddr<NodeId, Address>> for ConnectAddress {
|
||||
fn from(value: PeerAddr<NodeId, Address>) -> Self {
|
||||
|
|
@ -300,8 +344,9 @@ impl Deref for ConnectAddress {
|
|||
}
|
||||
|
||||
/// Peer configuration.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", tag = "type")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum PeerConfig {
|
||||
/// Static peer set. Connect to the configured peers and maintain the connections.
|
||||
Static,
|
||||
|
|
@ -316,8 +361,9 @@ impl Default for PeerConfig {
|
|||
}
|
||||
|
||||
/// Relay configuration.
|
||||
#[derive(Debug, Copy, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum Relay {
|
||||
/// Always relay messages.
|
||||
Always,
|
||||
|
|
@ -329,8 +375,9 @@ pub enum Relay {
|
|||
}
|
||||
|
||||
/// Proxy configuration.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", tag = "mode")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum AddressConfig {
|
||||
/// Proxy connections to this address type.
|
||||
Proxy {
|
||||
|
|
@ -343,8 +390,9 @@ pub enum AddressConfig {
|
|||
}
|
||||
|
||||
/// Default seeding policy. Applies when no repository policies for the given repo are found.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", tag = "default")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum DefaultSeedingPolicy {
|
||||
/// Allow seeding.
|
||||
Allow {
|
||||
|
|
@ -379,13 +427,19 @@ impl From<DefaultSeedingPolicy> for SeedingPolicy {
|
|||
}
|
||||
|
||||
/// Service configuration.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
derive(schemars::JsonSchema),
|
||||
schemars(rename = "NodeConfig")
|
||||
)]
|
||||
pub struct Config {
|
||||
/// Node alias.
|
||||
pub alias: Alias,
|
||||
/// Address to listen on.
|
||||
/// Socket address (a combination of IPv4 or IPv6 address and TCP port) to listen on.
|
||||
#[serde(default)]
|
||||
#[cfg_attr(feature = "schemars", schemars(example = &"127.0.0.1:8776"))]
|
||||
pub listen: Vec<net::SocketAddr>,
|
||||
/// Peer configuration.
|
||||
#[serde(default)]
|
||||
|
|
@ -409,6 +463,10 @@ pub struct Config {
|
|||
/// Log level.
|
||||
#[serde(default = "defaults::log")]
|
||||
#[serde(with = "crate::serde_ext::string")]
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
schemars(with = "crate::schemars_ext::log::Level")
|
||||
)]
|
||||
pub log: log::Level,
|
||||
/// Whether or not our node should relay messages.
|
||||
#[serde(default, deserialize_with = "crate::serde_ext::ok_or_default")]
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ pub struct FollowPolicy {
|
|||
}
|
||||
|
||||
/// Seeding policy of a node or repo.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", tag = "policy")]
|
||||
pub enum SeedingPolicy {
|
||||
/// Allow seeding.
|
||||
|
|
@ -159,6 +159,7 @@ impl TryFrom<&sqlite::Value> for Policy {
|
|||
/// Follow scope of a seeded repository.
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum Scope {
|
||||
/// Seed remotes that are explicitly followed.
|
||||
Followed,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ pub enum ConfigError {
|
|||
/// Local radicle configuration.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct Config {
|
||||
/// Public explorer. This is used for generating links.
|
||||
#[serde(default)]
|
||||
|
|
@ -360,3 +361,19 @@ impl From<String> for ConfigPath {
|
|||
ConfigPath(parts)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn schema() {
|
||||
use super::Config;
|
||||
use crate::prelude::Alias;
|
||||
use serde_json::to_value;
|
||||
|
||||
let schema = to_value(schemars::schema_for!(Config)).unwrap();
|
||||
let config = to_value(Config::new(Alias::new("schema"))).unwrap();
|
||||
jsonschema::validate(&schema, &config)
|
||||
.expect("generated configuration should validate under generated JSON Schema");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
//! This module contains auxiliary definitions for generating JSONSchemas.
|
||||
//! See <https://graham.cool/schemars/examples/5-remote_derive/>.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use schemars::JsonSchema;
|
||||
|
||||
pub(crate) mod log {
|
||||
use super::*;
|
||||
|
||||
/// See [`::log::Level`]
|
||||
#[derive(JsonSchema)]
|
||||
#[schemars(
|
||||
remote = "log::Level",
|
||||
description = "A log level.",
|
||||
rename_all = "UPPERCASE"
|
||||
)]
|
||||
pub(crate) enum Level {
|
||||
/// Designates very serious errors.
|
||||
Error,
|
||||
/// Designates hazardous situations.
|
||||
Warn,
|
||||
/// Designates useful information.
|
||||
Info,
|
||||
/// Designates lower priority information.
|
||||
Debug,
|
||||
/// Designates very low priority, often extremely verbose, information.
|
||||
Trace,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod bytesize {
|
||||
use super::*;
|
||||
|
||||
/// See [`::bytesize::ByteSize`] as well as [`::bytesize::parse`].
|
||||
/// Note that the pattern here is a little more restrictive than
|
||||
/// the actual parsing logic, as it enforces particular casing and whitespace.
|
||||
/// However, the regular expression is easier to read.
|
||||
#[derive(JsonSchema)]
|
||||
#[schemars(
|
||||
remote = "bytesize::ByteSize",
|
||||
description = "Byte quantities using unit prefixes according to SI or ISO/IEC 80000-13.",
|
||||
extend("examples" = ["7 G", "50.3 TiB", "200 B", "4 Ki", "10 MB"]),
|
||||
)]
|
||||
pub(crate) struct ByteSize(
|
||||
#[schemars(regex(pattern = r"^\d+(\.\d+)? ((K|M|G|T|P)i?B?|B)$"))] String,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) mod localtime {
|
||||
use super::*;
|
||||
|
||||
/// See [`::localtime::LocalDuration`]
|
||||
#[derive(JsonSchema)]
|
||||
#[schemars(
|
||||
remote = "localtime::LocalDuration",
|
||||
description = "A time duration measured locally in milliseconds."
|
||||
)]
|
||||
pub(crate) struct LocalDuration(u64);
|
||||
}
|
||||
|
|
@ -1,29 +1,36 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::prelude::RepoId;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// Web configuration.
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(
|
||||
feature = "schemars",
|
||||
derive(schemars::JsonSchema),
|
||||
schemars(rename = "WebConfig")
|
||||
)]
|
||||
pub struct Config {
|
||||
/// Pinned content.
|
||||
pub pinned: Pinned,
|
||||
/// URL pointing to an image used in the header of a node page.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "schemars", schemars(url))]
|
||||
pub banner_url: Option<String>,
|
||||
/// URL pointing to an image used as the node avatar.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "schemars", schemars(url))]
|
||||
pub avatar_url: Option<String>,
|
||||
/// Node description.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "schemars", schemars(url))]
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
/// Pinned content. This can be used to pin certain content when
|
||||
/// listing, e.g. pin repositories on a web client.
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Pinned {
|
||||
/// Pinned repositories.
|
||||
|
|
|
|||
Loading…
Reference in New Issue