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}