fix(trx-server): remove unused RigSdr import in rig_task.rs
Also run cargo fmt to fix formatting issues across trx-server, trx-frontend-http, and trx-configurator. https://claude.ai/code/session_01RsHUyVz2wjQjsEsxJo5owt Signed-off-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -217,7 +217,12 @@ fn check_server_sections(
|
||||
errors: &mut Vec<String>,
|
||||
) {
|
||||
if let Some(general) = table.get("general").and_then(|v| v.as_table()) {
|
||||
check_unknown_keys(general, SERVER_GENERAL_KEYS, &format!("{}[general].", prefix), warnings);
|
||||
check_unknown_keys(
|
||||
general,
|
||||
SERVER_GENERAL_KEYS,
|
||||
&format!("{}[general].", prefix),
|
||||
warnings,
|
||||
);
|
||||
validate_log_level(general, &format!("{}[general]", prefix), errors);
|
||||
validate_coordinates(general, &format!("{}[general]", prefix), errors);
|
||||
}
|
||||
@@ -225,13 +230,23 @@ fn check_server_sections(
|
||||
if let Some(rig) = table.get("rig").and_then(|v| v.as_table()) {
|
||||
check_unknown_keys(rig, RIG_KEYS, &format!("{}[rig].", prefix), warnings);
|
||||
if let Some(access) = rig.get("access").and_then(|v| v.as_table()) {
|
||||
check_unknown_keys(access, ACCESS_KEYS, &format!("{}[rig.access].", prefix), warnings);
|
||||
check_unknown_keys(
|
||||
access,
|
||||
ACCESS_KEYS,
|
||||
&format!("{}[rig.access].", prefix),
|
||||
warnings,
|
||||
);
|
||||
validate_access(access, &format!("{}[rig.access]", prefix), errors);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(listen) = table.get("listen").and_then(|v| v.as_table()) {
|
||||
check_unknown_keys(listen, LISTEN_KEYS, &format!("{}[listen].", prefix), warnings);
|
||||
check_unknown_keys(
|
||||
listen,
|
||||
LISTEN_KEYS,
|
||||
&format!("{}[listen].", prefix),
|
||||
warnings,
|
||||
);
|
||||
validate_port(listen, "port", &format!("{}[listen]", prefix), errors);
|
||||
}
|
||||
|
||||
@@ -241,7 +256,12 @@ fn check_server_sections(
|
||||
}
|
||||
|
||||
if let Some(behavior) = table.get("behavior").and_then(|v| v.as_table()) {
|
||||
check_unknown_keys(behavior, BEHAVIOR_KEYS, &format!("{}[behavior].", prefix), warnings);
|
||||
check_unknown_keys(
|
||||
behavior,
|
||||
BEHAVIOR_KEYS,
|
||||
&format!("{}[behavior].", prefix),
|
||||
warnings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,21 +272,41 @@ fn check_client_sections(
|
||||
errors: &mut Vec<String>,
|
||||
) {
|
||||
if let Some(general) = table.get("general").and_then(|v| v.as_table()) {
|
||||
check_unknown_keys(general, CLIENT_GENERAL_KEYS, &format!("{}[general].", prefix), warnings);
|
||||
check_unknown_keys(
|
||||
general,
|
||||
CLIENT_GENERAL_KEYS,
|
||||
&format!("{}[general].", prefix),
|
||||
warnings,
|
||||
);
|
||||
validate_log_level(general, &format!("{}[general]", prefix), errors);
|
||||
}
|
||||
|
||||
if let Some(remote) = table.get("remote").and_then(|v| v.as_table()) {
|
||||
check_unknown_keys(remote, REMOTE_KEYS, &format!("{}[remote].", prefix), warnings);
|
||||
check_unknown_keys(
|
||||
remote,
|
||||
REMOTE_KEYS,
|
||||
&format!("{}[remote].", prefix),
|
||||
warnings,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(frontends) = table.get("frontends").and_then(|v| v.as_table()) {
|
||||
check_unknown_keys(frontends, FRONTENDS_KEYS, &format!("{}[frontends].", prefix), warnings);
|
||||
check_unknown_keys(
|
||||
frontends,
|
||||
FRONTENDS_KEYS,
|
||||
&format!("{}[frontends].", prefix),
|
||||
warnings,
|
||||
);
|
||||
if let Some(http) = frontends.get("http").and_then(|v| v.as_table()) {
|
||||
validate_port(http, "port", &format!("{}[frontends.http]", prefix), errors);
|
||||
}
|
||||
if let Some(rigctl) = frontends.get("rigctl").and_then(|v| v.as_table()) {
|
||||
validate_port(rigctl, "port", &format!("{}[frontends.rigctl]", prefix), errors);
|
||||
validate_port(
|
||||
rigctl,
|
||||
"port",
|
||||
&format!("{}[frontends.rigctl]", prefix),
|
||||
errors,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,12 +325,21 @@ fn validate_log_level(table: &toml_edit::Table, context: &str, errors: &mut Vec<
|
||||
}
|
||||
|
||||
fn validate_coordinates(table: &toml_edit::Table, context: &str, errors: &mut Vec<String>) {
|
||||
if let Some(lat) = table.get("latitude").and_then(|v| v.as_float().or_else(|| v.as_integer().map(|i| i as f64))) {
|
||||
if let Some(lat) = table
|
||||
.get("latitude")
|
||||
.and_then(|v| v.as_float().or_else(|| v.as_integer().map(|i| i as f64)))
|
||||
{
|
||||
if !(-90.0..=90.0).contains(&lat) {
|
||||
errors.push(format!("{}.latitude {} is out of range (-90..90)", context, lat));
|
||||
errors.push(format!(
|
||||
"{}.latitude {} is out of range (-90..90)",
|
||||
context, lat
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(lon) = table.get("longitude").and_then(|v| v.as_float().or_else(|| v.as_integer().map(|i| i as f64))) {
|
||||
if let Some(lon) = table
|
||||
.get("longitude")
|
||||
.and_then(|v| v.as_float().or_else(|| v.as_integer().map(|i| i as f64)))
|
||||
{
|
||||
if !(-180.0..=180.0).contains(&lon) {
|
||||
errors.push(format!(
|
||||
"{}.longitude {} is out of range (-180..180)",
|
||||
@@ -309,12 +358,7 @@ fn validate_coordinates(table: &toml_edit::Table, context: &str, errors: &mut Ve
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_port(
|
||||
table: &toml_edit::Table,
|
||||
key: &str,
|
||||
context: &str,
|
||||
errors: &mut Vec<String>,
|
||||
) {
|
||||
fn validate_port(table: &toml_edit::Table, key: &str, context: &str, errors: &mut Vec<String>) {
|
||||
if let Some(port) = table.get(key).and_then(|v| v.as_integer()) {
|
||||
if let Some(enabled) = table.get("enabled").and_then(|v| v.as_bool()) {
|
||||
if enabled && port <= 0 {
|
||||
|
||||
@@ -12,7 +12,10 @@ use std::path::PathBuf;
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "trx-configurator", about = "Interactive configuration generator for trx-rs")]
|
||||
#[command(
|
||||
name = "trx-configurator",
|
||||
about = "Interactive configuration generator for trx-rs"
|
||||
)]
|
||||
struct Cli {
|
||||
/// Generate a default config without interactive prompts
|
||||
#[arg(long)]
|
||||
@@ -70,7 +73,10 @@ fn main() {
|
||||
"client" => ConfigType::Client,
|
||||
"combined" => ConfigType::Combined,
|
||||
other => {
|
||||
eprintln!("Unknown config type '{}'. Use: server, client, combined", other);
|
||||
eprintln!(
|
||||
"Unknown config type '{}'. Use: server, client, combined",
|
||||
other
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,11 @@ pub struct FrontendsSetup {
|
||||
// ── Prompt functions ────────────────────────────────────────────────────
|
||||
|
||||
pub fn prompt_config_type() -> ConfigType {
|
||||
let items = &["Server (trx-server.toml)", "Client (trx-client.toml)", "Combined (trx-rs.toml)"];
|
||||
let items = &[
|
||||
"Server (trx-server.toml)",
|
||||
"Client (trx-client.toml)",
|
||||
"Combined (trx-rs.toml)",
|
||||
];
|
||||
let sel = Select::new()
|
||||
.with_prompt("What configuration would you like to generate?")
|
||||
.items(items)
|
||||
|
||||
@@ -16,8 +16,7 @@ use crate::ConfigType;
|
||||
fn commented(table: &mut Table, key: &str, val: Item, comment: &str) {
|
||||
table.insert(key, val);
|
||||
if let Some(mut kv) = table.key_mut(key) {
|
||||
kv.leaf_decor_mut()
|
||||
.set_prefix(format!("# {}\n", comment));
|
||||
kv.leaf_decor_mut().set_prefix(format!("# {}\n", comment));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,24 +26,40 @@ fn header_comment(table: &mut Table, comment: &str) {
|
||||
|
||||
// ── Server document builder ─────────────────────────────────────────────
|
||||
|
||||
fn build_server_tables(
|
||||
general: ServerGeneral,
|
||||
rig: RigSetup,
|
||||
listen: ListenSetup,
|
||||
) -> Table {
|
||||
fn build_server_tables(general: ServerGeneral, rig: RigSetup, listen: ListenSetup) -> Table {
|
||||
let mut root = Table::new();
|
||||
|
||||
// [general]
|
||||
{
|
||||
let mut t = Table::new();
|
||||
header_comment(&mut t, "General settings");
|
||||
commented(&mut t, "callsign", value(&general.callsign), "Station callsign");
|
||||
commented(&mut t, "log_level", value(&general.log_level), "Log level (trace, debug, info, warn, error)");
|
||||
commented(
|
||||
&mut t,
|
||||
"callsign",
|
||||
value(&general.callsign),
|
||||
"Station callsign",
|
||||
);
|
||||
commented(
|
||||
&mut t,
|
||||
"log_level",
|
||||
value(&general.log_level),
|
||||
"Log level (trace, debug, info, warn, error)",
|
||||
);
|
||||
if let Some(lat) = general.latitude {
|
||||
commented(&mut t, "latitude", value(lat), "Station latitude (decimal degrees, WGS84)");
|
||||
commented(
|
||||
&mut t,
|
||||
"latitude",
|
||||
value(lat),
|
||||
"Station latitude (decimal degrees, WGS84)",
|
||||
);
|
||||
}
|
||||
if let Some(lon) = general.longitude {
|
||||
commented(&mut t, "longitude", value(lon), "Station longitude (decimal degrees, WGS84)");
|
||||
commented(
|
||||
&mut t,
|
||||
"longitude",
|
||||
value(lon),
|
||||
"Station longitude (decimal degrees, WGS84)",
|
||||
);
|
||||
}
|
||||
root.insert("general", Item::Table(t));
|
||||
}
|
||||
@@ -53,18 +68,38 @@ fn build_server_tables(
|
||||
{
|
||||
let mut t = Table::new();
|
||||
header_comment(&mut t, "Rig backend configuration");
|
||||
commented(&mut t, "model", value(&rig.model), "Rig model (ft817, ft450d, soapysdr)");
|
||||
commented(&mut t, "initial_freq_hz", value(144_300_000i64), "Initial frequency in Hz");
|
||||
commented(
|
||||
&mut t,
|
||||
"model",
|
||||
value(&rig.model),
|
||||
"Rig model (ft817, ft450d, soapysdr)",
|
||||
);
|
||||
commented(
|
||||
&mut t,
|
||||
"initial_freq_hz",
|
||||
value(144_300_000i64),
|
||||
"Initial frequency in Hz",
|
||||
);
|
||||
commented(&mut t, "initial_mode", value("USB"), "Initial mode");
|
||||
|
||||
// [rig.access]
|
||||
let mut access = Table::new();
|
||||
header_comment(&mut access, "Rig access method");
|
||||
commented(&mut access, "type", value(&rig.access_type), "Access type: serial, tcp, or sdr");
|
||||
commented(
|
||||
&mut access,
|
||||
"type",
|
||||
value(&rig.access_type),
|
||||
"Access type: serial, tcp, or sdr",
|
||||
);
|
||||
match rig.access_type.as_str() {
|
||||
"serial" => {
|
||||
if let Some(port) = &rig.serial_port {
|
||||
commented(&mut access, "port", value(port.as_str()), "Serial port path");
|
||||
commented(
|
||||
&mut access,
|
||||
"port",
|
||||
value(port.as_str()),
|
||||
"Serial port path",
|
||||
);
|
||||
}
|
||||
if let Some(baud) = rig.serial_baud {
|
||||
commented(&mut access, "baud", value(baud as i64), "Baud rate");
|
||||
@@ -80,7 +115,12 @@ fn build_server_tables(
|
||||
}
|
||||
"sdr" => {
|
||||
if let Some(args) = &rig.sdr_args {
|
||||
commented(&mut access, "args", value(args.as_str()), "SoapySDR device args string");
|
||||
commented(
|
||||
&mut access,
|
||||
"args",
|
||||
value(args.as_str()),
|
||||
"SoapySDR device args string",
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -93,10 +133,25 @@ fn build_server_tables(
|
||||
{
|
||||
let mut t = Table::new();
|
||||
header_comment(&mut t, "Polling and retry behavior");
|
||||
commented(&mut t, "poll_interval_ms", value(500i64), "Rig polling interval (ms)");
|
||||
commented(&mut t, "poll_interval_tx_ms", value(100i64), "Polling interval during TX (ms)");
|
||||
commented(
|
||||
&mut t,
|
||||
"poll_interval_ms",
|
||||
value(500i64),
|
||||
"Rig polling interval (ms)",
|
||||
);
|
||||
commented(
|
||||
&mut t,
|
||||
"poll_interval_tx_ms",
|
||||
value(100i64),
|
||||
"Polling interval during TX (ms)",
|
||||
);
|
||||
commented(&mut t, "max_retries", value(3i64), "Maximum retry attempts");
|
||||
commented(&mut t, "retry_base_delay_ms", value(100i64), "Base retry delay (ms)");
|
||||
commented(
|
||||
&mut t,
|
||||
"retry_base_delay_ms",
|
||||
value(100i64),
|
||||
"Base retry delay (ms)",
|
||||
);
|
||||
root.insert("behavior", Item::Table(t));
|
||||
}
|
||||
|
||||
@@ -105,8 +160,18 @@ fn build_server_tables(
|
||||
let mut t = Table::new();
|
||||
header_comment(&mut t, "JSON TCP listener for client connections");
|
||||
commented(&mut t, "enabled", value(true), "Enable the TCP listener");
|
||||
commented(&mut t, "listen", value(&listen.listen), "IP address to listen on");
|
||||
commented(&mut t, "port", value(listen.port as i64), "TCP port for client connections");
|
||||
commented(
|
||||
&mut t,
|
||||
"listen",
|
||||
value(&listen.listen),
|
||||
"IP address to listen on",
|
||||
);
|
||||
commented(
|
||||
&mut t,
|
||||
"port",
|
||||
value(listen.port as i64),
|
||||
"TCP port for client connections",
|
||||
);
|
||||
root.insert("listen", Item::Table(t));
|
||||
}
|
||||
|
||||
@@ -118,22 +183,34 @@ fn build_server_tables(
|
||||
commented(&mut t, "listen", value("127.0.0.1"), "Audio listen address");
|
||||
commented(&mut t, "port", value(4531i64), "Audio TCP port");
|
||||
commented(&mut t, "sample_rate", value(48000i64), "Sample rate in Hz");
|
||||
commented(&mut t, "channels", value(2i64), "Channel count (1 = mono, 2 = stereo)");
|
||||
commented(&mut t, "frame_duration_ms", value(20i64), "Opus frame duration (ms)");
|
||||
commented(&mut t, "bitrate_bps", value(256000i64), "Opus bitrate (bps)");
|
||||
commented(
|
||||
&mut t,
|
||||
"channels",
|
||||
value(2i64),
|
||||
"Channel count (1 = mono, 2 = stereo)",
|
||||
);
|
||||
commented(
|
||||
&mut t,
|
||||
"frame_duration_ms",
|
||||
value(20i64),
|
||||
"Opus frame duration (ms)",
|
||||
);
|
||||
commented(
|
||||
&mut t,
|
||||
"bitrate_bps",
|
||||
value(256000i64),
|
||||
"Opus bitrate (bps)",
|
||||
);
|
||||
root.insert("audio", Item::Table(t));
|
||||
}
|
||||
|
||||
root
|
||||
}
|
||||
|
||||
pub fn build_server(
|
||||
general: ServerGeneral,
|
||||
rig: RigSetup,
|
||||
listen: ListenSetup,
|
||||
) -> DocumentMut {
|
||||
pub fn build_server(general: ServerGeneral, rig: RigSetup, listen: ListenSetup) -> DocumentMut {
|
||||
let mut doc = DocumentMut::new();
|
||||
doc.decor_mut().set_prefix("# trx-server configuration\n# Generated by trx-configurator\n");
|
||||
doc.decor_mut()
|
||||
.set_prefix("# trx-server configuration\n# Generated by trx-configurator\n");
|
||||
let tables = build_server_tables(general, rig, listen);
|
||||
for (key, item) in tables.iter() {
|
||||
doc.insert(key, item.clone());
|
||||
@@ -154,8 +231,18 @@ fn build_client_tables(
|
||||
{
|
||||
let mut t = Table::new();
|
||||
header_comment(&mut t, "General settings");
|
||||
commented(&mut t, "callsign", value(&general.callsign), "Station callsign");
|
||||
commented(&mut t, "log_level", value(&general.log_level), "Log level (trace, debug, info, warn, error)");
|
||||
commented(
|
||||
&mut t,
|
||||
"callsign",
|
||||
value(&general.callsign),
|
||||
"Station callsign",
|
||||
);
|
||||
commented(
|
||||
&mut t,
|
||||
"log_level",
|
||||
value(&general.log_level),
|
||||
"Log level (trace, debug, info, warn, error)",
|
||||
);
|
||||
root.insert("general", Item::Table(t));
|
||||
}
|
||||
|
||||
@@ -163,8 +250,18 @@ fn build_client_tables(
|
||||
{
|
||||
let mut t = Table::new();
|
||||
header_comment(&mut t, "Remote server connection");
|
||||
commented(&mut t, "url", value(&remote.url), "Server address (host:port)");
|
||||
commented(&mut t, "poll_interval_ms", value(750i64), "State poll interval (ms)");
|
||||
commented(
|
||||
&mut t,
|
||||
"url",
|
||||
value(&remote.url),
|
||||
"Server address (host:port)",
|
||||
);
|
||||
commented(
|
||||
&mut t,
|
||||
"poll_interval_ms",
|
||||
value(750i64),
|
||||
"State poll interval (ms)",
|
||||
);
|
||||
|
||||
let mut auth = Table::new();
|
||||
if let Some(token) = &remote.token {
|
||||
@@ -182,26 +279,61 @@ fn build_client_tables(
|
||||
header_comment(&mut frontends_table, "Frontend configuration");
|
||||
|
||||
let mut http = Table::new();
|
||||
commented(&mut http, "enabled", value(frontends.http_enabled), "Enable HTTP web frontend");
|
||||
commented(
|
||||
&mut http,
|
||||
"enabled",
|
||||
value(frontends.http_enabled),
|
||||
"Enable HTTP web frontend",
|
||||
);
|
||||
commented(&mut http, "listen", value("127.0.0.1"), "Listen address");
|
||||
commented(&mut http, "port", value(frontends.http_port as i64), "HTTP port");
|
||||
commented(
|
||||
&mut http,
|
||||
"port",
|
||||
value(frontends.http_port as i64),
|
||||
"HTTP port",
|
||||
);
|
||||
frontends_table.insert("http", Item::Table(http));
|
||||
|
||||
let mut rigctl = Table::new();
|
||||
commented(&mut rigctl, "enabled", value(frontends.rigctl_enabled), "Enable Hamlib rigctl frontend");
|
||||
commented(
|
||||
&mut rigctl,
|
||||
"enabled",
|
||||
value(frontends.rigctl_enabled),
|
||||
"Enable Hamlib rigctl frontend",
|
||||
);
|
||||
commented(&mut rigctl, "listen", value("127.0.0.1"), "Listen address");
|
||||
commented(&mut rigctl, "port", value(frontends.rigctl_port as i64), "rigctl port");
|
||||
commented(
|
||||
&mut rigctl,
|
||||
"port",
|
||||
value(frontends.rigctl_port as i64),
|
||||
"rigctl port",
|
||||
);
|
||||
frontends_table.insert("rigctl", Item::Table(rigctl));
|
||||
|
||||
let mut http_json = Table::new();
|
||||
commented(&mut http_json, "enabled", value(true), "Enable JSON-over-TCP frontend");
|
||||
commented(&mut http_json, "listen", value("127.0.0.1"), "Listen address");
|
||||
commented(
|
||||
&mut http_json,
|
||||
"enabled",
|
||||
value(true),
|
||||
"Enable JSON-over-TCP frontend",
|
||||
);
|
||||
commented(
|
||||
&mut http_json,
|
||||
"listen",
|
||||
value("127.0.0.1"),
|
||||
"Listen address",
|
||||
);
|
||||
commented(&mut http_json, "port", value(0i64), "Port (0 = ephemeral)");
|
||||
frontends_table.insert("http_json", Item::Table(http_json));
|
||||
|
||||
let mut audio = Table::new();
|
||||
commented(&mut audio, "enabled", value(true), "Enable audio client");
|
||||
commented(&mut audio, "server_port", value(4531i64), "Server audio port");
|
||||
commented(
|
||||
&mut audio,
|
||||
"server_port",
|
||||
value(4531i64),
|
||||
"Server audio port",
|
||||
);
|
||||
frontends_table.insert("audio", Item::Table(audio));
|
||||
|
||||
root.insert("frontends", Item::Table(frontends_table));
|
||||
@@ -216,7 +348,8 @@ pub fn build_client(
|
||||
frontends: FrontendsSetup,
|
||||
) -> DocumentMut {
|
||||
let mut doc = DocumentMut::new();
|
||||
doc.decor_mut().set_prefix("# trx-client configuration\n# Generated by trx-configurator\n");
|
||||
doc.decor_mut()
|
||||
.set_prefix("# trx-client configuration\n# Generated by trx-configurator\n");
|
||||
let tables = build_client_tables(general, remote, frontends);
|
||||
for (key, item) in tables.iter() {
|
||||
doc.insert(key, item.clone());
|
||||
@@ -235,7 +368,8 @@ pub fn build_combined(
|
||||
frontends: FrontendsSetup,
|
||||
) -> DocumentMut {
|
||||
let mut doc = DocumentMut::new();
|
||||
doc.decor_mut().set_prefix("# trx-rs combined configuration\n# Generated by trx-configurator\n");
|
||||
doc.decor_mut()
|
||||
.set_prefix("# trx-rs combined configuration\n# Generated by trx-configurator\n");
|
||||
|
||||
let server = build_server_tables(s_general, rig, listen);
|
||||
let mut server_item = Item::Table(server);
|
||||
@@ -294,7 +428,9 @@ pub fn build_default(config_type: ConfigType) -> DocumentMut {
|
||||
match config_type {
|
||||
ConfigType::Server => build_server(s_general, rig, listen),
|
||||
ConfigType::Client => build_client(c_general, remote, frontends),
|
||||
ConfigType::Combined => build_combined(s_general, rig, listen, c_general, remote, frontends),
|
||||
ConfigType::Combined => {
|
||||
build_combined(s_general, rig, listen, c_general, remote, frontends)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +448,8 @@ pub fn write_file(doc: &DocumentMut, path: &Path) -> Result<(), String> {
|
||||
}
|
||||
}
|
||||
|
||||
std::fs::write(path, doc.to_string()).map_err(|e| format!("Failed to write {}: {}", path.display(), e))?;
|
||||
std::fs::write(path, doc.to_string())
|
||||
.map_err(|e| format!("Failed to write {}: {}", path.display(), e))?;
|
||||
println!("Wrote {}", path.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user