[dependencies]
log = "0.4.14"
simple_logger = "2.1.0"
time = "0.3.7"
初始化
use log::LevelFilter;
use simple_logger::SimpleLogger;
use time::UtcOffset;
pub fn init_log() {
SimpleLogger::new()
.with_level(LevelFilter::Info)
.with_colors(true)
.with_utc_offset(UtcOffset::from_hms(8, 0, 0).unwrap())
.init()
.unwrap();
}
[dependencies]
log = "0.4.0"
env_logger = "0.9.0"
// 简单形式:默认UTC 0 时区
use env_logger::Env;
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
// use chrono::prelude::*;
// use env_logger::Env;
// use std::io::Write;
// 自定义时间+日志级别颜色
let env = Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info");
env_logger::Builder::from_env(env)
.format(|buf, record| {
let level = { buf.default_styled_level(record.level()) };
writeln!(
buf,
"{} {} [{}:{}] {}",
Local::now().format("%Y-%m-%d %H:%M:%S"),
level,
record.module_path().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
&record.args()
)
})
.init();
特别注意:从控制台输入,会隐含读入\n换行符等,需要进行trim处理,否则使用match等方法匹配失败。
// 标准输出
println
// 标准错误
eprintln
// 输入
let mut input = String::new();
std::io::stdin()
.read_line(&mut input)
.expect("输入错误!");
// 输出
println!("你输入的内容是 : {}", input);
let user_choice = menu_handle();
// 通过String.as_str()方法可快速转换,此处为处理换行符,调用trim方法,trim方法隐含有转&str的能力
match user_choice.trim() {
"1" => println!("用户选择1"),
"2" => println!("用户选择2"),
_ => println!("用户选择功能项无效"),
}
print!("输入文件名:");
解析:rust标准输出是行缓冲的,只有到了行尾才显示,想要输出需要手动刷新。
io::stdout().flush();
use std::thread::sleep;
// 线程睡眠
sleep(Duration::from_secs(1));
calamine = "0.18.0"
// calamine = "0.18.0"
fn data_read(file_path: String) {
//Excel读取
let mut excel: Xlsx<_> = open_workbook(file_path).expect("打开Excel文件失败!");
if let Some(Ok(r)) = excel.worksheet_range_at(0) {
let (h, w) = r.get_size();
log::info!("读取到Excel文件共{}行, {}列", h, w);
let mut title_vec: Vec<String> = Vec::new();
for i in 0..w {
let title_name = r.get_value((0, i as u32)).unwrap().to_string();
title_vec.push(title_name);
}
println!("titles:{:?}", title_vec);
// 跳过首行标题行
let rows = r.rows().skip(1);
for row in rows {
println!("row[5]={:?}", row[5]);
}
}
}
let num = input.trim().parse::<i32>().unwrap();
colored = "2.0.0"
"this is blue".blue();
"this is red".red();
"this is red on blue".red().on_blue();
"this is also red on blue".on_blue().red();
"you can use truecolor values too!".truecolor(0, 255, 136);
"background truecolor also works :)".on_truecolor(135, 28, 167);
"bright colors are welcome as well".on_bright_blue().bright_red();
"you can also make bold comments".bold();
println!("{} {} {}", "or use".cyan(), "any".italic().yellow(), "string type".cyan());
"or change advice. This is red".yellow().blue().red();
"or clear things up. This is default color and style".red().bold().clear();
"purple and magenta are the same".purple().magenta();
"and so are normal and clear".normal().clear();
"you can specify color by string".color("blue").on_color("red");
String::from("this also works!").green().bold();
format!("{:30}", "format works as expected. This will be padded".blue());
format!("{:.3}", "and this will be green but truncated to 3 chars".green());
//注意:find查出是索引号,中文存储UTF8编码,一个中文占3个字节。
//总长度
let len = company.len_utf8();
let index = company_name.find("区").unwrap();
let end = min(company_name.len(), index + 3 * 3);
company_name[index + 3..end].to_string()
let handle = tokio::spawn(async move {
sleep(Duration::from_secs(interval_time as u64));
});
handle.await.unwrap();
reqwest = { version = "0.11", features = "blocking"}
// reqwest可以使用阻塞方式
let body = reqwest::blocking::get(url).unwrap().text().unwrap();
// 网络文件下载
pub async fn download(url: &str) -> Vec<u8> {
let response = reqwest::get(url).await.unwrap();
let bytes = response.bytes().await.unwrap();
let data: Result<Vec<_>, _> = bytes.bytes().collect();
data.unwrap()
}
/// 保存文件到本地(二进制字节)
#[allow(dead_code)]
pub fn save_file_to_local(path: &Path, b: &[u8]) {
let p = Path::new(path);
if !p.exists() {
let parent_path = p.parent().unwrap();
fs::create_dir_all(parent_path).expect("创建文件夹失败");
}
let mut f = File::create(p).unwrap();
let wr = f.write_all(b);
match wr {
Ok(_) => {
info!(
"保存文件到本地[{}]成功,文件大小:{}字节",
path.to_str().unwrap(),
b.len()
);
}
Err(e) => {
error!(
"保存文件到本地[{}]失败,文件大小:{}字节,错误信息:{}",
path.to_str().unwrap(),
b.len(),
e
);
}
}
}
html2md = "0.2"
html2md::parse_html(&body);
clap = "3.0.0-beta.4"
# actix-web
# rocket
# warp
# axum
cargo install cargo-watch
cargo watch -x 'run --bin api-server'
# https://actix.rs/docs/databases/
# https://github.com/actix/examples/tree/master/databases/diesel
# https://diesel.rs/guides/getting-started.html
diesel = { version = "2.0.0", features = ["sqlite", "r2d2"] }
# Web技术栈:tauri
# 原生 GUI:druid、iced 和 sixtyfps。
//Vec HashMap都会扩容
//显式调用shrink_to_fit方法,可缩小容量
# 目前较活跃的库:用纯Rust编写的十进制实现,适合财务计算
rust_decimal = "1.25.0"
# decimal类型默认serde格式化为str,需要格式化为float需指定feature
rust_decimal = { version = "1.25.0", features = [ "serde-float" ] }
// Rust 支持几乎所有主流的数据库,包括但不限于 MySQL、Postgres、Redis、RocksDB、Cassandra、MongoDB、ScyllaDB、CouchDB 等等
// 使用 ORM,可以用: diesel,或者 sea-orm。
// 如果你享受直接但安全的 SQL 查询,可以使用 sqlx。
// 连接池:r2d2
// sqlx框架
// sqlx = { version = "0.6", features = [ "runtime-tokio-native-tls", "mysql", "chrono" ] }
// MySQL连接池初始化
let pool = MySqlPool::connect("mysql://root:123456@127.0.0.1/book")
.await
.unwrap();
//Rust 有对标 puppeteer 的 headless_chrome
//以及对标 selenium 的 thirtyfour 和 fantoccini。
// chrome_driver下载地址:https://chromedriver.chromium.org/downloads
// 初始化chrome_driver连接客户端
async fn init_chrome_driver_client() -> Client {
let cr = ClientBuilder::native()
.connect("http://localhost:4444")
.await;
return match cr {
Ok(c) => c,
Err(e) => {
error!(
"连接Chrome Driver驱动服务失败,请确认启动Chrome Driver驱动,\
启动命令参考:./chromedriver.exe --port=4444,错误信息:{}",
e
);
panic!("连接Chrome Driver驱动服务失败,程序终止");
}
};
}
// fantoccini使用
let c = init_chrome_driver_client().await;
c.goto("SITE_BASE_URL").await.unwrap();
let element = c.find(Locator::Css(".nav")).await.unwrap();
fantoccini:适用于标准WebDriver协议
thirtyfour:适用于chromedriver(基于fantoccini的更上层的库)
# 封装好的chromedriver docker镜像 105版本及对应chromedriver 默认仅允许127.0.0.1连接
# 启动时可指定IP环境变量(为空时允许所有IP连接),PORT端口等
docker run -d --name chromedriver -p 4444:4444 -e IPS= seekerman/chromedriver:latest
/// rust示例
// thirtyfour = "0.31.0"
// tokio = { version ="1.17.0", features = ["full"] }
use thirtyfour::prelude::*;
#[tokio::main]
async fn main() -> WebDriverResult<()> {
let mut caps = DesiredCapabilities::chrome();
caps.add_chrome_arg("--disable-gpu").unwrap();
caps.add_chrome_arg("--headless").unwrap();
caps.add_chrome_arg("--no-sandbox").unwrap();
let driver = WebDriver::new("http://localhost:4444", caps).await?;
driver.goto("https://www.baidu.com").await?;
let source = driver.source().await.unwrap();
println!("source:{}", source);
// Always explicitly close the browser.
driver.quit().await?;
Ok(())
}
# 使用selenium Server
docker run --rm -d -p 4444:4444 -p 5900:5900 --name selenium-server -v /dev/shm:/dev/shm selenium/standalone-chrome:4.1.0-20211123
// rust连接selenium server
use thirtyfour::prelude::*;
use tokio;
#[tokio::main]
async fn main() -> color_eyre::Result<()> {
// The use of color_eyre gives much nicer error reports, including making
// it much easier to locate where the error occurred.
color_eyre::install()?;
let caps = DesiredCapabilities::chrome();
let driver = WebDriver::new("http://localhost:4444", caps).await?;
// Navigate to https://wikipedia.org.
driver.goto("https://wikipedia.org").await?;
let elem_form = driver.find(By::Id("search-form")).await?;
// Always explicitly close the browser. There are no async destructors.
driver.quit().await?;
Ok(())
}
// 处理 Kubernetes API 的:kube-rs
// 用 wasm-pack 和 wasm-bindgen,不但生成 wasm,同时还生成 ts/js 调用 wasm 的代码
nipper = "0.1.9"
tendril = "0.4.2"
regex = "1.5.6"
/// 正则处理内容,多空格自动替换
pub async fn content_handle(content: &str) -> String {
let r = Regex::new(r"\s+").unwrap();
let res = r.replace_all(content, " ");
res.to_string()
}
// 引入依赖:async-recursion = "1.0.0"
// 递归方法增加:#[async_recursion(?Send)]
let mut hasher = DefaultHasher::new();
url.hash(&mut hasher);
let id = hasher.finish();
# serde: required if you are going to use documents
serde = { version="1.0", features = ["derive"] }
# serde_json: required in some parts of this guide
serde_json = "1.0"
#[derive(Serialize, Deserialize)]
dotenv = "0.15.0"
use dotenv::dotenv;
use std::env;
fn main() {
dotenv().ok();
for (key, value) in env::vars() {
println!("{}: {}", key, value);
}
}
[dependencies]
# 邮件发送 smtp协议
lettre = "0.9"
lettre_email = "0.9"
# 邮件接收pop3协议
pop3 = "1.0.6"
# 邮件接收协议
imap = "2.4.1"
# email解析
# lettre下的MimeMessage::parse() 目前不稳定
# email-parser待验证
email-parser = "0.5.0"
案例:https://www.codeleading.com/article/3341645849/
邮件发送乱码问题,在Message::builder()
中添加.header(ContentType::TEXT_PLAIN)
,这将指定ContentType
为text/plain; charset=utf-8
。
github的issues中有提到:https://github.com/lettre/lettre/pull/841
源码ContentType文件中也要有相关文档描述:
/// A `ContentType` of type `text/plain; charset=utf-8`
///
/// Indicates that the body is in utf-8 encoded plain text.
pub const TEXT_PLAIN: ContentType = Self::from_mime(mime::TEXT_PLAIN_UTF_8);
/// A `ContentType` of type `text/html; charset=utf-8`
///
/// Indicates that the body is in utf-8 encoded html.
pub const TEXT_HTML: ContentType = Self::from_mime(mime::TEXT_HTML_UTF_8);
可自行编译安装,较为繁琐。
或vcpkg进行安装(未验证成功)
# 指定vendored特征,可免安装,但依赖perl进行编译,下载:https://strawberryperl.com/
openssl = { version = "0.10.41", features = ["vendored"] }
sudo apt-get install openssl
sudo apt-get install libssl-dev
crate:https://crates.io/crates/procfs
doc:https://docs.rs/procfs/0.13.2/procfs/
github:https://github.com/eminence/procfs
# 该库为读取/proc下的文件解析
procfs = "0.13.2"
# 跨平台,获取操作系统信息,包括:kernel/cpu/memory/disk/load/hostname。
sys-info = "0.9.1"
依赖glib2.0:
# error:Package glib-2.0 was not found in the pkg-config search path
# ubuntu
sudo apt-get install libglib2.0-dev
# centos
sudo yum install glib2-devel
rusqlite = { version = "0.28.0", features = ["bundled"] }
# on OpenSUSE
sudo zypper install sqlite3-devel libsqlite3-0 sqlite3
# on Ubuntu
sudo apt-get install libsqlite3-dev sqlite3
# on Fedora
sudo dnf install libsqlite3x-devel sqlite3x
# on macOS (using homebrew)
brew install sqlite3
基于ubuntu构建包含sqlite3的docker镜像:
FROM ubuntu:20.04
WORKDIR /app
copy ./api-server ./api-server
RUN apt-get install -y update \
&& apt-get install -y upgrade \
&& apt-get install -y sqlite3 libsqlite3-dev
ENV LANG=C.UTF-8
VOLUME /app/data
EXPOSE ${PORT}
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
ENTRYPOINT /app/api-server
# The default when using vcpkg is to dynamically link, which must be enabled by setting # # 设置环境变量 VCPKGRS_DYNAMIC=1
# environment variable before build. vcpkg install sqlite3:x64-windows will install the required library.
vcpkg install sqlite3:x64-windows
cargo install vcpkg_cli
# 用vcpkg_cli检验编译好后的sqlite3是否能被rust识别
vcpkg_cli probe sqlite3
例如:home_dir,cache_dir,config_dir,data_dir,desktop_dir,download_dir等等
可以使用的库:dirs 或者 directories
dirs = "4.0.0"
directories = "4.0.1"
https://crates.io/crates/daemonize
https://crates.io/crates/fork
daemonize = "0.4.1"
fork = "0.1.19"
users: Library for accessing Unix users and groups
这是一个用于访问 Unix 用户和组的库。 它支持获取系统用户和组,将它们存储在缓存中,并创建您自己的模拟表。
users = "0.11.0"
打印出当前用户名的完整示例:
use users::{get_user_by_uid, get_current_uid};
let user = get_user_by_uid(get_current_uid()).unwrap();
let username = user.name().to_str().unwrap().to_string();
let group_id = user.primary_group_id() as i32;
println!("Hello, {}!", username);
# crontab,已经多年没维护了,不可用
# 新框架:delay-timer,试用有一些bug,github作者称比较忙
delay_timer = "0.11.3"
# 中文社区,群友作者,简单实用
rcron = "1.1.3"
# Bollard: an asynchronous rust client library for the docker API
bollard = "0.13.0"
// uuid
// uuid = { version = "1.1.2", features = ["v4"] }
/// uuid
pub fn generate_uuid() -> String {
let id = Uuid::new_v4();
id.to_string()
}
Rust中编写递归比较麻烦,该类问题更多的可以采用loop结构进行处理。
rustup toolchain list
rustup toolchain install nightly
# 更改当前系统默认的channel
rustup default nightly
# 使用rustup run指定channel
rustup run nightly cargo build
# 使用rustup overwrite设置当前项目使用的channel
rustup override set nightly
// mac
use std::process::Command;
fn main( ) {
println!( "Opening" );
Command::new( "open" )
.arg( "." ) // <- Specify the directory you'd like to open.
.spawn( )
.unwrap( );
}
// windows
use std::process::Command;
fn main( ) {
println!( "Opening" );
Command::new( "explorer" )
.arg( "." ) // <- Specify the directory you'd like to open.
.spawn( )
.unwrap( );
}
//linux
use std::process::Command;
fn main( ) {
println!( "Opening" );
Command::new( "xdg-open" )
.arg( "." ) // <- Specify the directory you'd like to open.
.spawn( )
.unwrap( );
}
dotenv = "0.15.0"
dotenv().ok();
let secret = env::var("JWT_SECRET").expect("请设置JWT_SECRET环境变量");
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "rust-demo=info,tower_http=info".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
/// 注意:rust-demo为项目名时,配置处需使用rust_demo,转为下划线
use tracing::{error, info, warn};
info!("entry....");
# 默认使用的是openssl,安装麻烦,可使用上文的内置静态包,也可以使用rustls-tls
reqwest = { version = "0.11.9", features = ["json", "rustls-tls"], default-features = false }
rust-crypto = "^0.2"
use crypto::digest::Digest;
use crypto::sha1::Sha1;
/// sha1 encrypt
pub fn get_sha1(s: &str) -> String {
let mut hasher = Sha1::new();
hasher.input_str(s);
let hex = hasher.result_str();
hex
}
# 可参考axum官方jwt例子
jsonwebtoken = "8.0"
urlencoding = "2.1.2"
use urlencoding::encode;
let encoded = encode("This string will be URL encoded.");
println!("{}", encoded);
Tantivy是Rust实现的本地搜索库,功能对标lucene
。