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        #[arg(long, value_name = "SECS")]
109        timeout: Option<u64>,
110    },
111
112    /// Show the config
113    Config {
114        /// Only show external addresses from the node's config
115        #[arg(long)]
116        addresses: bool,
117    },
118
119    /// Interact with the node database
120    #[command(subcommand, hide = true)]
121    Db(DbOperation),
122
123    /// Watch and print events.
124    ///
125    /// This command will connect to the node and print events to
126    /// standard output as they occur.
127    ///
128    /// If no timeout or count is specified, it will run indefinitely.
129    Events {
130        /// How long to wait to receive an event before giving up
131        #[arg(long, value_name = "SECS")]
132        timeout: Option<u64>,
133
134        /// Exit after <COUNT> events
135        #[arg(long, short = 'n')]
136        count: Option<usize>,
137    },
138
139    /// Show the routing table
140    Routing {
141        /// Output the routing table as json
142        #[arg(long)]
143        json: bool,
144
145        /// Show the routing table entries for the given RID
146        #[arg(long)]
147        rid: Option<RepoId>,
148
149        /// Show the routing table entries for the given NID
150        #[arg(long)]
151        nid: Option<NodeId>,
152    },
153
154    /// Start the node
155    Start {
156        /// Start the node in the foreground
157        #[arg(long)]
158        foreground: bool,
159
160        /// Verbose output
161        #[arg(long, short)]
162        verbose: bool,
163
164        /// Start node binary at path
165        #[arg(long, default_value = "radicle-node")]
166        path: PathBuf,
167
168        /// Additional options to pass to the binary
169        ///
170        /// See `radicle-node --help` for additional options
171        #[arg(value_name = "NODE_OPTIONS", last = true, num_args = 1..)]
172        options: Vec<OsString>,
173    },
174
175    /// Show the log
176    Logs {
177        /// Only show <COUNT> lines of the log
178        #[arg(long, value_name = "COUNT", default_value_t = 60)]
179        lines: usize,
180    },
181
182    /// Show the status
183    Status {
184        /// If node is running, only print the Node ID and exit, otherwise exit with a non-zero exit status.
185        #[arg(long, value_parser = OnlyParser)]
186        only: Option<Only>,
187    },
188
189    /// Manage the inventory
190    Inventory {
191        /// List the inventory of the given NID, defaults to `self`
192        #[arg(long)]
193        nid: Option<NodeId>,
194    },
195
196    /// Show debug information related to the running node.
197    ///
198    /// This includes metrics fetching, peer connections, rate limiting, etc.
199    Debug,
200
201    /// Show the active sessions of the running node.
202    ///
203    /// Deprecated, use `status` instead.
204    #[command(hide = true)]
205    Sessions,
206
207    /// Stop the node
208    Stop,
209}
210
211impl Default for Command {
212    fn default() -> Self {
213        Command::Status { only: None }
214    }
215}
216
217/// Operations related to the [`Command::Db`]
218#[derive(Debug, Subcommand)]
219pub(super) enum DbOperation {
220    /// Execute an SQL operation on the local node database.
221    ///
222    /// The command only returns the number of rows that are affected by the
223    /// query. This means that `SELECT` queries will not return their output.
224    ///
225    /// The command should only be used for executing queries given you know
226    /// what you are doing.
227    Exec {
228        #[arg(value_name = "SQL")]
229        query: String,
230    },
231}