Rust 将字段内容从一个公共结构复制到另一个公共结构

Rust 将字段内容从一个公共结构复制到另一个公共结构,rust,Rust,我正在修改另一方编写的一些代码。其目的是模拟UniFi安全网关,以在UniFi控制器软件中获取报告。我计划在Zotac Mini PC上运行仿真,其中2个NIC运行CentOS 8。为了不必担心额外的NAT,我将2个NIC设置为桥接器 每个网络设备的结构定义为: #[派生(PartialEq、克隆、调试)] 发布(板条箱)结构UnixNetworkDevice{ 名称:String, mac:MacAddr, 接口:网络接口, 统计:UnixNetworkDeviceStatistics, }

我正在修改另一方编写的一些代码。其目的是模拟UniFi安全网关,以在UniFi控制器软件中获取报告。我计划在Zotac Mini PC上运行仿真,其中2个NIC运行CentOS 8。为了不必担心额外的NAT,我将2个NIC设置为桥接器

每个网络设备的结构定义为:

#[派生(PartialEq、克隆、调试)]
发布(板条箱)结构UnixNetworkDevice{
名称:String,
mac:MacAddr,
接口:网络接口,
统计:UnixNetworkDeviceStatistics,
}
当为eth0设备填写结构时,您会得到以下信息(
wan\u device
开头只是我的标签,所以我知道我在看什么):

我还从桥接器设备中提取相同的信息:

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_设备
不显示任何IP地址,因为IP地址已分配给网桥

现在,当它尝试对
wan\u设备
执行
clone()
时,由于结构中的
ips
部分为空,因此失败。我想将结构的IPS部分从
bri\U设备
复制到
wan\U设备
。我希望这是可能的

下面是一个可以制作成项目的MRE,包括它后面的Cargo.toml文件

这是指向相同的ZIP文件的链接:

#[宏使用]
外部板条箱日志;
外部板条箱(静态);
外部板条箱;
外部板条箱正则表达式;
外部板条箱简易记录器;
///用于通知的网络接口
#[派生(序列化、反序列化、PartialEq、克隆、调试、默认)]
发布结构OpnFiInformNetworkInterface{
酒吧滴:使用,
酒吧启用:bool,
酒吧全双工:布尔,
酒吧入口:Vec,
pub ip:String,
酒吧延迟:usize,
酒吧麦克:字符串,
酒吧名称:String,
发布名称服务器:Vec,
pub-netmask:String,
pub num_port:usize,
pub rx_字节:usize,
酒吧rx_:使用,
发布接收错误:usize,
pub rx_多播:usize,
酒吧rx_包:使用,
酒吧速度:usize,
pub speedtest_lastrun:使用,
酒吧速度测试:使用,
发布速度测试_状态:字符串,
pub tx_字节:usize,
pub tx_已删除:usize,
发布发送错误:usize,
pub tx_数据包:usize,
酒吧:布尔,
酒吧正常运行时间:usize,
pub xput_down:使用,
pub exput\u up:使用,
}
使用serde::{反序列化,序列化};
#[派生(序列化、反序列化、PartialEq、克隆、调试)]
发布(板条箱)结构配置{
发布通知url:String,
发布能力:Vec,
发布cfgversion:String,
pub selfrun_guest_模式:字符串,
发布led_已启用:bool,
pub stunu url:String,
发布管理url:String,
pub authkey:String,
酒吧使用:bool,
酒吧报道:bool,
}
使用pnet::{
数据链接::{interfaces,NetworkInterface},
util::MacAddr,
};
使用std::str::FromStr;
使用std::{fs,io,path};
#[派生(PartialEq、克隆、调试)]
发布(板条箱)结构UnixNetworkDevice{
名称:String,
mac:MacAddr,
接口:网络接口,
统计:UnixNetworkDeviceStatistics,
}
impl UnixNetworkDevice{
pub fn new(名称:&String)->io::Result{
让device_path=path::path::new(“/sys/class/net”).join(name);
if!device_path.as_path()是_dir(){
返回Err(io::Error::new)(
io::ErrorKind::InvalidInput,
格式!(“找不到UnixNetworkDevice{},名称),
));
}
让mac_string=fs::read_to_string(device_path.join(“address”)?;
如果mac_string.trim().len()<15{
返回Err(io::Error::new)(
io::ErrorKind::InvalidData,
“Mac地址为空。”,
));
}
让mac=MacAddr::from_str(mac_string.trim())
.map|u err(| e | io::Error::new(io::ErrorKind::Other,e))?;
让interface=interfaces().into|iter().filter(|i | i.name==*name).next();
if接口.is_none(){
返回Err(io::Error::new)(
io::ErrorKind::InvalidInput,
格式!(“无法找到网络接口{}”,名称),
));
}
让interface=interface.unwrap();
让statistics=UnixNetworkDeviceStatistics::new(名称);
正常(UnixNetworkDevice{
名称:name.clone(),
雨衣,
接口,
统计数字,,
})
}
发布fn名称(&self)->字符串{
self.name.clone()
}
发布fn mac(&self)->MacAddr{
self.mac
}
发布fn接口(&self)->网络接口{
self.interface.clone()
}
发布fn统计(和自我)->UnixNetworkDeviceStatistics{
self.statistics.clone()
}
}
//====统计数据=====
#[派生(PartialOrd、PartialEq、克隆、调试)]
发布(板条箱)结构UnixNetworkDeviceStatistics{
pub冲突:usize,
酒吧多播:usize,
pub rx_字节:usize,
pub rx_压缩:usize,
发布接收crc错误:usize,
酒吧rx_:使用,
发布接收错误:usize,
酒吧接收fifo错误:使用,
发布接收帧错误:usize,
发布接收长度错误:usize,
酒吧接收错误:usize,
酒吧接待员:使用,
pub rx_over_错误:usize,
酒吧rx_包:使用,
发布发送中止错误:usize,
pub tx_字节:usize,
发布发送载体错误:usize,
pub tx_compressed:usize,
pub tx_已删除:usize,
发布发送错误:usize,
发布发送fifo错误:使用,
发布发送信号错误:usize,
pub tx_数据包:usize,
发布发送窗口错误:usize,
}
impl Unix网络设备统计{
pub fn new(设备名称:&String)->UnixNetworkDeviceStatistics{
让read_value=| statistic_name:&str |->io::Result{
让stat_path=path::path::new(“/sys/class/net”)
.join(设备名称)
.join(“统计”)
.加入(统计名称);
让value=fs::读取字符串(stat_path.as_path())?;
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
    }
})
#[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"