cfun 0.2.11

Tidy up common functions
Documentation
#[cfg(feature = "net")]
use std::net::IpAddr;

#[cfg(feature = "net")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "net")]
use sysinfo::Networks;
#[derive(Debug, Serialize, Deserialize)]
#[cfg(feature = "net")]
pub struct IpAddress {
    pub interface: String,
    pub ip: IpAddr,
    pub prefix: u8,
}

#[derive(Debug, Serialize, Deserialize)]
#[cfg(feature = "net")]
pub struct IpV4Address {
    pub interface: String,
    pub octets: [u8; 4],
    pub prefix: u8,
}

/// get all ip address
#[cfg(feature = "net")]
pub fn list_ip_address() -> Vec<IpAddress> {
    let networks = Networks::new_with_refreshed_list();
    let mut address = Vec::new();
    for (name, network) in networks.iter() {
        for ip in network.ip_networks() {
            address.push(IpAddress {
                interface: name.clone(),
                ip: ip.addr,
                prefix: ip.prefix,
            });
        }
    }
    address
}

/// get all ipv4 address
#[cfg(feature = "net")]
pub fn list_ipv4() -> Vec<IpV4Address> {
    let networks = Networks::new_with_refreshed_list();
    let mut address = Vec::new();
    for (name, network) in networks.iter() {
        for ip in network.ip_networks() {
            if let IpAddr::V4(addr) = ip.addr {
                address.push(IpV4Address {
                    interface: name.clone(),
                    prefix: ip.prefix,
                    octets: addr.octets(),
                });
            }
        }
    }
    address
}

/// send get request save to file
#[cfg(feature = "net")]
pub fn block_download_file(
    url: &str,
    save_path: &str,
    callback: Option<fn(cur: u64, total: u64)>,
) -> Result<(), String> {
    use reqwest::{blocking::Client, header::CONTENT_LENGTH};
    use std::{
        fs::File,
        io::{Read, Write},
        path::Path,
        sync::OnceLock,
    };
    static CLIENT: OnceLock<Client> = OnceLock::new();
    let client = CLIENT.get_or_init(Client::new);
    let response = client.head(url).send().map_err(|e| e.to_string())?; // 先获取文件大小

    let total_size = if let Some(size) = response.headers().get(CONTENT_LENGTH) {
        size.to_str()
            .map_err(|e| e.to_string())?
            .parse::<u64>()
            .unwrap_or(0)
    } else {
        0
    };

    let mut response = client.get(url).send().map_err(|e| e.to_string())?; // 开始下载
    let mut file = File::create(Path::new(save_path)).map_err(|e| e.to_string())?;

    let mut downloaded: u64 = 0;
    let mut buffer = [0; 8192]; // 8KB 缓冲区
    while let Ok(n) = response.read(&mut buffer) {
        if n == 0 {
            break;
        }
        file.write_all(&buffer[..n]).map_err(|e| e.to_string())?;
        downloaded += n as u64;
        if let Some(callback) = callback {
            callback(downloaded, total_size);
        }
    }
    Ok(())
}

/// send get request save to file
#[cfg(feature = "net")]
pub async fn download_file(
    url: &str,
    save_path: &str,
    callback: Option<fn(cur: u64, total: u64)>,
) -> Result<(), String> {
    use reqwest::{header::CONTENT_LENGTH, Client};
    use std::{fs::File, io::Write, path::Path, sync::OnceLock};
    static CLIENT: OnceLock<Client> = OnceLock::new();
    let client = CLIENT.get_or_init(Client::new);
    let response = client.head(url).send().await.map_err(|e| e.to_string())?; // 先获取文件大小

    let total_size = if let Some(size) = response.headers().get(CONTENT_LENGTH) {
        size.to_str()
            .map_err(|e| e.to_string())?
            .parse::<u64>()
            .unwrap_or(0)
    } else {
        0
    };

    let mut response = client.get(url).send().await.map_err(|e| e.to_string())?; // 开始下载
    let mut file = File::create(Path::new(save_path)).map_err(|e| e.to_string())?;

    let mut downloaded: u64 = 0;

    while let Some(bytes) = response.chunk().await.map_err(|e| e.to_string())? {
        file.write_all(&bytes).map_err(|e| e.to_string())?;
        downloaded += bytes.len() as u64;
        if let Some(callback) = callback {
            callback(downloaded, total_size);
        }
    }
    Ok(())
}

/// send get request to get content as string
#[cfg(feature = "net")]
pub fn block_download_string(url: &str) -> Result<String, String> {
    use reqwest::blocking::Client;
    use std::sync::OnceLock;

    static CLIENT: OnceLock<Client> = OnceLock::new();
    let client = CLIENT.get_or_init(Client::new);
    let response = client.get(url).send().map_err(|e| e.to_string())?; // 开始下载
    let data = response.bytes().map_err(|e| e.to_string())?;
    let data = String::from_utf8(data.to_vec()).map_err(|e| e.to_string())?;
    Ok(data)
}

/// send get request to get content as string
#[cfg(feature = "net")]
pub async fn download_string(url: &str) -> Result<String, String> {
    use reqwest::Client;
    use std::sync::OnceLock;

    static CLIENT: OnceLock<Client> = OnceLock::new();
    let client = CLIENT.get_or_init(Client::new);
    let response = client.get(url).send().await.map_err(|e| e.to_string())?; // 开始下载
    let data = response.bytes().await.map_err(|e| e.to_string())?;
    let data = String::from_utf8(data.to_vec()).map_err(|e| e.to_string())?;
    Ok(data)
}

/// send get request to get content as json
#[cfg(feature = "net")]
pub fn block_download_json<T>(url: &str) -> Result<T, String>
where
    T: serde::de::DeserializeOwned,
{
    use reqwest::blocking::Client;
    use std::sync::OnceLock;

    static CLIENT: OnceLock<Client> = OnceLock::new();
    let client = CLIENT.get_or_init(Client::new);
    let response = client.get(url).send().map_err(|e| e.to_string())?; // 开始下载
    let data: T = response.json().map_err(|e| e.to_string())?;
    Ok(data)
}

/// send get request to get content as json
#[cfg(feature = "net")]
pub async fn download_json<T>(url: &str) -> Result<T, String>
where
    T: serde::de::DeserializeOwned,
{
    use std::sync::OnceLock;

    use reqwest::Client;
    static CLIENT: OnceLock<Client> = OnceLock::new();
    let client = CLIENT.get_or_init(Client::new);
    let response = client.get(url).send().await.map_err(|e| e.to_string())?; // 开始下载
    let data: T = response.json().await.map_err(|e| e.to_string())?;
    Ok(data)
}

/// block post request
#[cfg(feature = "net")]
pub fn block_post_json<T>(url: &str, data: T) -> Result<reqwest::blocking::Response, reqwest::Error>
where
    T: serde::Serialize,
{
    use std::sync::OnceLock;

    use reqwest::blocking::Client;
    static CLIENT: OnceLock<Client> = OnceLock::new();
    let client = CLIENT.get_or_init(Client::new);
    let resp = client.post(url).json(&data).send()?;
    Ok(resp)
}

/// post request
#[cfg(feature = "net")]
pub async fn post_json<T>(url: &str, data: T) -> Result<reqwest::Response, reqwest::Error>
where
    T: serde::Serialize,
{
    use std::sync::OnceLock;

    use reqwest::Client;
    static CLIENT: OnceLock<Client> = OnceLock::new();
    let client = CLIENT.get_or_init(Client::new);
    let resp = client.post(url).json(&data).send().await?;
    Ok(resp)
}

#[test]
#[cfg(feature = "net")]
fn test_list_ip_address() {
    let ret = list_ip_address();
    println!("{:#?}", ret);
}