Skip to main content

radicle_cli/commands/node/
args.rs

1use std::ffi::OsString;
2use std::fmt::Debug;
3use std::path::PathBuf;
4use std::str::FromStr;
5
6use thiserror::Error;
7
8use clap::{Parser, Subcommand};
9
10use radicle::crypto::{PublicKey, PublicKeyError};
11use radicle::node::{Address, NodeId, PeerAddr, PeerAddrParseError};
12use radicle::prelude::RepoId;
13
14const ABOUT: &str = "Control and query the Radicle Node";
15
16#[derive(Parser, Debug)]
17#[command(about = ABOUT, long_about, disable_version_flag = true)]
18pub struct Args {
19    #[command(subcommand)]
20    pub(super) command: Option<Command>,
21}
22
23/// Address used for the [`Operation::Connect`]
24#[derive(Clone, Debug)]
25pub(super) enum Addr {
26    /// Fully-specified address of the form `<NID>@<ADDR>`
27    Peer(PeerAddr<NodeId, Address>),
28    /// Just the `NID`, to be used for address lookups.
29    Node(NodeId),
30}
31
32#[derive(Error, Debug)]
33pub(super) enum AddrParseError {
34    #[error("{0}, expected <NID> or <NID>@<ADDR>")]
35    PeerAddr(#[from] PeerAddrParseError<PublicKey>),
36    #[error(transparent)]
37    NodeId(#[from] PublicKeyError),
38}
39
40impl FromStr for Addr {
41    type Err = AddrParseError;
42
43    fn from_str(s: &str) -> Result<Self, Self::Err> {
44        if s.contains("@") {
45            PeerAddr::from_str(s)
46                .map(Self::Peer)
47                .map_err(AddrParseError::PeerAddr)
48        } else {
49            NodeId::from_str(s)
50                .map(Self::Node)
51                .map_err(AddrParseError::NodeId)
52        }
53    }
54}
55
56#[derive(Clone, Debug)]
57pub enum Only {
58    Nid,
59}
60
61#[derive(Error, Debug)]
62#[error("could not parse value `{0}`")]
63pub struct OnlyParseError(String);
64
65impl FromStr for Only {
66    type Err = OnlyParseError;
67
68    fn from_str(value: &str) -> Result<Self, Self::Err> {
69        match value {
70            "nid" => Ok(Only::Nid),
71            _ => Err(OnlyParseError(value.to_string())),
72        }
73    }
74}
75
76#[derive(Clone, Debug)]
77struct OnlyParser;
78
79impl clap::builder::TypedValueParser for OnlyParser {
80    type Value = Only;
81
82    fn parse_ref(
83        &self,
84        cmd: &clap::Command,
85        arg: Option<&clap::Arg>,
86        value: &std::ffi::OsStr,
87    ) -> Result<Self::Value, clap::Error> {
88        <Only as std::str::FromStr>::from_str.parse_ref(cmd, arg, value)
89    }
90
91    fn possible_values(
92        &self,
93    ) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
94        use clap::builder::PossibleValue;
95        Some(Box::new([PossibleValue::new("nid")].into_iter()))
96    }
97}
98
99#[derive(Subcommand, Debug)]
100pub(super) enum Command {
101    /// Instruct the node to connect to another node
102    Connect {
103        /// The Node ID, and optionally the address and port, of the node to connect to
104        #[arg(value_name = "NID[@ADDR]")]
105        addr: Addr,
106
107        /// How long to wait for the connection to be established
108        ///
109        /// Valid arguments are for example "10s", "5min" or "2h 37min"
110        #[arg(long, value_parser = humantime::parse_duration)]
111        timeout: Option<std::time::Duration>,
112    },
113
114    /// Show the config
115    Config {
116        /// Only show external addresses from the node's config
117        #[arg(long)]
118        addresses: bool,
119    },
120
121    /// Interact with the node database
122    #[command(subcommand, hide = true)]
123    Db(DbOperation),
124
125    /// Watch and print events.
126    ///
127    /// This command will connect to the node and print events to
128    /// standard output as they occur.
129    ///
130    /// If no timeout or count is specified, it will run indefinitely.
131    Events {
132        /// How long to wait to receive an event before giving up
133        ///
134        /// Valid arguments are for example "10s", "5min" or "2h 37min"
135        #[arg(long, value_parser = humantime::parse_duration)]
136        timeout: Option<std::time::Duration>,
137
138        /// Exit after <COUNT> events
139        #[arg(long, short = 'n')]
140        count: Option<usize>,
141    },
142
143    /// Show the routing table
144    Routing {
145        /// Output the routing table as json
146        #[arg(long)]
147        json: bool,
148
149        /// Show the routing table entries for the given RID
150        #[arg(long)]
151        rid: Option<RepoId>,
152
153        /// Show the routing table entries for the given NID
154        #[arg(long)]
155        nid: Option<NodeId>,
156    },
157
158    /// Start the node
159    Start {
160        /// Start the node in the foreground
161        #[arg(long)]
162        foreground: bool,
163
164        /// Verbose output
165        #[arg(long, short)]
166        verbose: bool,
167
168        /// Start node binary at path
169        #[arg(long, default_value = "radicle-node")]
170        path: PathBuf,
171
172        /// Additional options to pass to the binary
173        ///
174        /// See `radicle-node --help` for additional options
175        #[arg(value_name = "NODE_OPTIONS", last = true, num_args = 1..)]
176        options: Vec<OsString>,
177    },
178
179    /// Show the log
180    Logs {
181        /// Only show <COUNT> lines of the log
182        #[arg(long, value_name = "COUNT", default_value_t = 60)]
183        lines: usize,
184    },
185
186    /// Show the status
187    Status {
188        /// If node is running, only print the Node ID and exit, otherwise exit with a non-zero exit status.
189        #[arg(long, value_parser = OnlyParser)]
190        only: Option<Only>,
191    },
192
193    /// Manage the inventory
194    Inventory {
195        /// List the inventory of the given NID, defaults to `self`
196        #[arg(long)]
197        nid: Option<NodeId>,
198    },
199
200    /// Show debug information related to the running node.
201    ///
202    /// This includes metrics fetching, peer connections, rate limiting, etc.
203    Debug,
204
205    /// Show the active sessions of the running node.
206    ///
207    /// Deprecated, use `status` instead.
208    #[command(hide = true)]
209    Sessions,
210
211    /// Stop the node
212    Stop,
213}
214
215impl Default for Command {
216    fn default() -> Self {
217        Command::Status { only: None }
218    }
219}
220
221/// Operations related to the [`Command::Db`]
222#[derive(Debug, Subcommand)]
223pub(super) enum DbOperation {
224    /// Execute an SQL operation on the local node database.
225    ///
226    /// The command only returns the number of rows that are affected by the
227    /// query. This means that `SELECT` queries will not return their output.
228    ///
229    /// The command should only be used for executing queries given you know
230    /// what you are doing.
231    Exec {
232        #[arg(value_name = "SQL")]
233        query: String,
234    },
235}