rust - 将字段内容从一个公共结构复制到另一个
问题描述
我正在修改一些由另一方编写的代码。目的是模拟 UniFi 安全网关以在 UniFi 控制器软件中获取报告。我计划在带有 2 个运行 CentOS 8 的 NIC 的 Zotac Mini PC 上运行仿真。所以我不必担心额外的 NAT,我将 2 个 NIC 设置为桥接器。
每个网络设备的结构定义为:
#[derive(PartialEq, Clone, Debug)]
pub(crate) struct UnixNetworkDevice {
name: String,
mac: MacAddr,
interface: NetworkInterface,
statistics: UnixNetworkDeviceStatistics,
}
当为 eth0 设备填写结构时,您会得到这个(wan_device
开头只是我的标签,所以我知道我在看什么):
wan_device Some(UnixNetworkDevice {
name: "eth0",
mac: 00:01:2e:80:3e:1d,
interface: NetworkInterface {
name: "eth0",
index: 2,
mac: Some(00:01:2e:80:3e:1d),
ips: [],
flags: 69699
},
statistics: UnixNetworkDeviceStatistics {
collisions: 0,
multicast: 497,
rx_bytes: 359185,
rx_compressed: 0,
rx_crc_errors: 0,
rx_dropped: 26,
rx_errors: 0,
rx_fifo_errors: 0,
rx_frame_errors: 0,
rx_length_errors: 0,
rx_missed_errors: 0,
rx_nohandler: 0,
rx_over_errors: 0,
rx_packets: 1587,
tx_aborted_errors: 0,
tx_bytes: 74695,
tx_carrier_errors: 0,
tx_compressed: 0,
tx_dropped: 0,
tx_errors: 0,
tx_fifo_errors: 0,
tx_heartbeat_errors: 0,
tx_packets: 474,
tx_window_errors: 0
}
})
我还从 Bridge 设备中提取相同的信息:
bri_device Some(UnixNetworkDevice {
name: "bri0",
mac: 00:01:2e:80:3e:1d,
interface: NetworkInterface {
name: "bri0",
index: 5,
mac: Some(00:01:2e:80:3e:1d),
ips: [V4(Ipv4Network { addr: 192.168.113.2, prefix: 24 }),
V6(Ipv6Network { addr: fe80::c8ee:a0ff:fe3a:3096, prefix: 64 })],
flags: 69699
},
statistics: UnixNetworkDeviceStatistics {
collisions: 0,
multicast: 0,
rx_bytes: 275467,
rx_compressed: 0,
rx_crc_errors: 0,
rx_dropped: 0,
rx_errors: 0,
rx_fifo_errors: 0,
rx_frame_errors: 0,
rx_length_errors: 0,
rx_missed_errors: 0,
rx_nohandler: 0,
rx_over_errors: 0,
rx_packets: 1371,
tx_aborted_errors: 0,
tx_bytes: 68355,
tx_carrier_errors: 0,
tx_compressed: 0,
tx_dropped: 0,
tx_errors: 0,
tx_fifo_errors: 0,
tx_heartbeat_errors: 0,
tx_packets: 345,
tx_window_errors: 0
}
})
wan_device
不显示任何 IP 地址,因为它已分配给网桥。
现在,当它尝试执行 a clone()
of the时,由于结构中的空白部分而wan_device
失败。ips
我想将结构的 IPS 部分从复制bri_device
到wan_device
. 我希望这是可能的。
这是一个可以做成项目的 MRE,包括它后面的 Cargo.toml 文件。
这是相同 ZIP 文件的链接: https ://www.dropbox.com/s/7pfxbmv49ra1jxs/Test-OpnFi.zip?dl=0
#[macro_use]
extern crate log;
extern crate lazy_static;
extern crate clap;
extern crate regex;
extern crate simple_logger;
/// Network interface for inform
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug, Default)]
pub struct OpnFiInformNetworkInterface {
pub drops: usize,
pub enabled: bool,
pub full_duplex: bool,
pub gateways: Vec<String>,
pub ip: String,
pub latency: usize,
pub mac: String,
pub name: String,
pub nameservers: Vec<String>,
pub netmask: String,
pub num_port: usize,
pub rx_bytes: usize,
pub rx_dropped: usize,
pub rx_errors: usize,
pub rx_multicast: usize,
pub rx_packets: usize,
pub speed: usize,
pub speedtest_lastrun: usize,
pub speedtest_ping: usize,
pub speedtest_status: String,
pub tx_bytes: usize,
pub tx_dropped: usize,
pub tx_errors: usize,
pub tx_packets: usize,
pub up: bool,
pub uptime: usize,
pub xput_down: usize,
pub xput_up: usize,
}
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
pub(crate) struct Config {
pub inform_url: String,
pub capability: Vec<String>,
pub cfgversion: String,
pub selfrun_guest_mode: String,
pub led_enabled: bool,
pub stun_url: String,
pub mgmt_url: String,
pub authkey: String,
pub use_aes_gcm: bool,
pub report_crash: bool,
}
use pnet::{
datalink::{interfaces, NetworkInterface},
util::MacAddr,
};
use std::str::FromStr;
use std::{fs, io, path};
#[derive(PartialEq, Clone, Debug)]
pub(crate) struct UnixNetworkDevice {
name: String,
mac: MacAddr,
interface: NetworkInterface,
statistics: UnixNetworkDeviceStatistics,
}
impl UnixNetworkDevice {
pub fn new(name: &String) -> io::Result<UnixNetworkDevice> {
let device_path = path::Path::new("/sys/class/net").join(name);
if !device_path.as_path().is_dir() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unable to locate UnixNetworkDevice {}", name),
));
}
let mac_string = fs::read_to_string(device_path.join("address"))?;
if mac_string.trim().len() < 15 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Mac address is empty.",
));
}
let mac = MacAddr::from_str(mac_string.trim())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
let interface = interfaces().into_iter().filter(|i| i.name == *name).next();
if interface.is_none() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unable to locate NetworkInterface {}", name),
));
}
let interface = interface.unwrap();
let statistics = UnixNetworkDeviceStatistics::new(name);
Ok(UnixNetworkDevice {
name: name.clone(),
mac,
interface,
statistics,
})
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn mac(&self) -> MacAddr {
self.mac
}
pub fn interface(&self) -> NetworkInterface {
self.interface.clone()
}
pub fn statistics(&self) -> UnixNetworkDeviceStatistics {
self.statistics.clone()
}
}
// ===== Statistics =====
#[derive(PartialOrd, PartialEq, Clone, Debug)]
pub(crate) struct UnixNetworkDeviceStatistics {
pub collisions: usize,
pub multicast: usize,
pub rx_bytes: usize,
pub rx_compressed: usize,
pub rx_crc_errors: usize,
pub rx_dropped: usize,
pub rx_errors: usize,
pub rx_fifo_errors: usize,
pub rx_frame_errors: usize,
pub rx_length_errors: usize,
pub rx_missed_errors: usize,
pub rx_nohandler: usize,
pub rx_over_errors: usize,
pub rx_packets: usize,
pub tx_aborted_errors: usize,
pub tx_bytes: usize,
pub tx_carrier_errors: usize,
pub tx_compressed: usize,
pub tx_dropped: usize,
pub tx_errors: usize,
pub tx_fifo_errors: usize,
pub tx_heartbeat_errors: usize,
pub tx_packets: usize,
pub tx_window_errors: usize,
}
impl UnixNetworkDeviceStatistics {
pub fn new(device_name: &String) -> UnixNetworkDeviceStatistics {
let read_value = |statistic_name: &str| -> io::Result<usize> {
let stat_path = path::Path::new("/sys/class/net")
.join(device_name)
.join("statistics")
.join(statistic_name);
let value = fs::read_to_string(stat_path.as_path())?;
usize::from_str(value.trim())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
};
UnixNetworkDeviceStatistics {
collisions: read_value("collisions").unwrap_or_default(),
multicast: read_value("multicast").unwrap_or_default(),
rx_bytes: read_value("rx_bytes").unwrap_or_default(),
rx_compressed: read_value("rx_compressed").unwrap_or_default(),
rx_crc_errors: read_value("rx_crc_errors").unwrap_or_default(),
rx_dropped: read_value("rx_dropped").unwrap_or_default(),
rx_errors: read_value("rx_errors").unwrap_or_default(),
rx_fifo_errors: read_value("rx_fifo_errors").unwrap_or_default(),
rx_frame_errors: read_value("rx_frame_errors").unwrap_or_default(),
rx_length_errors: read_value("rx_length_errors").unwrap_or_default(),
rx_missed_errors: read_value("rx_missed_errors").unwrap_or_default(),
rx_nohandler: read_value("rx_nohandler").unwrap_or_default(),
rx_over_errors: read_value("rx_over_errors").unwrap_or_default(),
rx_packets: read_value("rx_packets").unwrap_or_default(),
tx_aborted_errors: read_value("tx_aborted_errors").unwrap_or_default(),
tx_bytes: read_value("tx_bytes").unwrap_or_default(),
tx_carrier_errors: read_value("tx_carrier_errors").unwrap_or_default(),
tx_compressed: read_value("tx_compressed").unwrap_or_default(),
tx_dropped: read_value("tx_dropped").unwrap_or_default(),
tx_errors: read_value("tx_errors").unwrap_or_default(),
tx_fifo_errors: read_value("tx_fifo_errors").unwrap_or_default(),
tx_heartbeat_errors: read_value("tx_heartbeat_errors").unwrap_or_default(),
tx_packets: read_value("tx_packets").unwrap_or_default(),
tx_window_errors: read_value("tx_window_errors").unwrap_or_default(),
}
}
}
impl From<UnixNetworkDevice> for OpnFiInformNetworkInterface {
fn from(value: UnixNetworkDevice) -> Self {
let interface = value.interface();
let stats = value.statistics();
let ip = interface
.ips
.iter()
.filter(|ip| ip.is_ipv4())
.next()
.unwrap();
Self {
drops: stats.rx_dropped + stats.tx_dropped,
enabled: true,
full_duplex: true,
gateways: vec![],
ip: ip.ip().to_string(),
latency: 1,
mac: value.mac().to_string(),
name: value.name().to_string(),
nameservers: vec![],
netmask: ip.mask().to_string(),
num_port: interface.index as usize,
rx_bytes: stats.rx_bytes,
rx_dropped: stats.rx_dropped,
rx_errors: stats.rx_errors,
rx_multicast: 0,
rx_packets: stats.rx_packets,
speed: 1000,
speedtest_lastrun: 0,
speedtest_ping: 0,
speedtest_status: "Idle".to_string(),
tx_bytes: stats.tx_bytes,
tx_dropped: stats.tx_dropped,
tx_errors: stats.tx_errors,
tx_packets: stats.tx_packets,
up: true,
uptime: 0,
xput_down: 0,
xput_up: 0,
}
}
}
fn main() {
let matches = clap::App::new("OpnFi Device")
.version("0.1.0")
.author("James Parks <jrjparks@zathera.com>")
.about("Emulates a UniFi device")
.arg(
clap::Arg::with_name("config")
.short("c")
.long("config")
.value_name("FILE")
.help("Sets a config file path to use")
.takes_value(true),
)
.arg(
clap::Arg::with_name("controller")
.long("controller")
.value_name("FILE")
.help("FQDN or IP Address of UniFi Controller")
.takes_value(true),
)
.arg(
clap::Arg::with_name("wan")
.short("w")
.long("wan")
.value_name("NIC")
.help("Set the nic to report for WAN")
.takes_value(true)
.default_value("eth0"),
)
.arg(
clap::Arg::with_name("lan")
.short("l")
.long("lan")
.value_name("NIC")
.help("Set the nic to report for LAN")
.takes_value(true)
.default_value("eth1"),
)
.arg(
clap::Arg::with_name("bri")
.short("b")
.long("bri")
.value_name("NIC")
.help("Set the nic to report for BRIDGE")
.takes_value(true)
.default_value("bri0"),
)
.get_matches();
let _wan_device = match matches.value_of("wan") {
Some(wan_name) => {
info!("Using {} as WAN device.", wan_name);
UnixNetworkDevice::new(&wan_name.to_string()).ok()
}
None => None,
};
let _lan_device = match matches.value_of("lan") {
Some(lan_name) => {
info!("Using {} as LAN device.", lan_name);
UnixNetworkDevice::new(&lan_name.to_string()).ok()
}
None => None,
};
let _bri_device = match matches.value_of("bri") {
Some(bri_name) => {
info!("Using {} as BRIDGE device.", bri_name);
UnixNetworkDevice::new(&bri_name.to_string()).ok()
}
None => None,
};
println!("wan_device {:?}", _wan_device);
println!("lan_device {:?}", _lan_device);
println!("bri_device {:?}", _bri_device);
// Interfaces
println!("Wan_Interface");
let _wan_interface: Option<OpnFiInformNetworkInterface> = match &mut _wan_device.as_ref()
{
Some(wan) => Some(wan.clone().into()),
_ => None,
};
println!("Lan_Interface");
let _lan_interface: Option<OpnFiInformNetworkInterface> = match &mut _lan_device.as_ref()
{
Some(lan) => Some(lan.clone().into()),
_ => None,
};
println!("Bri_Interface");
let _bri_interface: Option<OpnFiInformNetworkInterface> = match &mut _bri_device.as_ref()
{
Some(bri) => Some(bri.clone().into()),
_ => None,
};
}
[package]
name = "test_opnfi"
version = "0.0.1"
authors = ["Mike Schaffner <mcschaffner@gmail.com>"]
edition = "2018"
license = "Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lazy_static = "1.4.0"
toml = "0.5.5"
regex = "1.3.1"
rand = "0.7.2"
net2 = "0.2.33"
byteorder = "1.3.2"
serde = "1.0.103"
serde_json = "1.0.42"
sysinfo = "0.9.6"
reqwest = "0.9"
hex = "0.4.0"
pnet = "0.23.0"
clap = "2.33"
ctrlc = "3.1.3"
log = "0.4.8"
simple_logger = "1.3.0"
解决方案
您的问题涉及许多特定于生锈的问题。类型、类型的大小、生存时间、可选值等等。
请阅读 Shepmaster 的评论并更新您的问题。给你一个可能有帮助的方向:https ://gist.github.com/rust-play/2289bbcf50caabe7bc0fb397e073e581
// #[derive(Clone)] dosn't work because the unknown size of `value`
struct A {
value: [isize],
}
#[derive(Clone)]
struct B<'a> {
value: Option<&'a [isize]>,
}
fn main() {
//let a: A = A { value: [1] };
//let a1 = a.clone();
let b: B = B { value: None };
let b1 = b.clone();
}
推荐阅读
- android - RxJava、Retrofit、Android - 等到所有请求完成
- python-3.x - 我正在编写比较 date.print 的代码首先,如果第一个更大(或)打印第二个,如果第二个更大,如果日期无效则出错
- asterisk - Asterisk 广播机制
- bash - 如何在 .bash_profile macOS 中获取外部文件
- ios - 发布时 Xamarin.Forms 项目 iOS 中没有网络连接
- python - 三个列表的交叉点并保存所有交叉点?(不是严格的交集)
- c# - 将图像上传到 SharePoint 文档库 C#
- firebase - React Native Flatlist 只渲染一行
- sql - 如何将 SQL 转换为 lambda 或 LINQ
- c# - 特定字体系列的标签不会出现