gear_node_wrapper/
node.rs

1// This file is part of Gear.
2//
3// Copyright (C) 2024-2025 Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Gear protocol node wrapper
20use crate::{Log, NodeInstance, utils};
21use anyhow::Result;
22use std::{
23    env,
24    path::Path,
25    process::{Command, Stdio},
26};
27
28const GEAR_BINARY: &str = "gear";
29const DEFAULT_ARGS: [&str; 4] = ["--dev", "--tmp", "--no-hardware-benchmarks", "--rpc-port"];
30
31/// Gear protocol node wrapper
32pub struct Node {
33    /// Node command
34    command: Command,
35    /// The rpc port of the node if any
36    port: Option<u16>,
37    /// How many logs should the log holder stores
38    logs: Option<usize>,
39}
40
41impl Node {
42    /// Create a new from gear command that found
43    /// in the current system.
44    pub fn new() -> Result<Self> {
45        Self::from_path(which::which(GEAR_BINARY)?)
46    }
47
48    /// Create a new node from path
49    pub fn from_path(path: impl AsRef<Path>) -> Result<Self> {
50        Ok(Self {
51            command: Command::new(path.as_ref()),
52            port: None,
53            logs: None,
54        })
55    }
56
57    /// Append argument to the node
58    ///
59    /// see also [`Node::args`]
60    pub fn arg(&mut self, arg: &str) -> &mut Self {
61        self.command.arg(arg);
62        self
63    }
64
65    /// Append arguments to the node
66    ///
67    /// NOTE: argument `--dev` is managed by [`Node::spawn`] and could not be removed, if
68    /// you are about to run a production node, please run the node binary directly.
69    pub fn args(&mut self, args: &[&str]) -> &mut Self {
70        self.command.args(args);
71        self
72    }
73
74    /// Sets the rpc port and returns self.
75    pub fn rpc_port(&mut self, port: u16) -> &mut Self {
76        self.port = Some(port);
77        self
78    }
79
80    /// The log holder stores 256 lines of matched logs
81    /// by default, here in this function we receive a limit
82    /// of the logs and resize the logger on spawning.
83    pub fn logs(&mut self, limit: usize) -> &mut Self {
84        self.logs = Some(limit);
85        self
86    }
87
88    /// Spawn the node
89    pub fn spawn(&mut self) -> Result<NodeInstance> {
90        let port = self.port.unwrap_or(utils::pick()).to_string();
91        let mut args = DEFAULT_ARGS.to_vec();
92        args.push(&port);
93
94        let mut process = self
95            .command
96            .env(
97                "RUST_LOG",
98                env::var("RUST_LOG").unwrap_or_else(|_| "".into()),
99            )
100            .args(args)
101            .stderr(Stdio::piped())
102            .stdout(Stdio::piped())
103            .spawn()?;
104
105        let address = format!("{}:{port}", utils::LOCALHOST).parse()?;
106        let mut log = Log::new(self.logs);
107        log.spawn(&mut process)?;
108        Ok(NodeInstance {
109            address,
110            log,
111            process,
112        })
113    }
114}