Subsections of Libraries
Anyhow Error Handler
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΡΠ°Π±ΠΎΡΡ Ρ ΠΎΡΠΈΠ±ΠΊΠ°ΠΌΠΈ - https://docs.rs/anyhow/latest/anyhow/
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ Π΅Π΄ΠΈΠ½ΡΠΉ ΡΠΈΠΏ Error Π΄Π»Ρ Π²ΡΠ΅Ρ
ΠΎΡΠΈΠ±ΠΎΠΊ;
- ΠΠΎΠ±Π°Π²Π»ΡΠ΅Ρ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡ ΠΊ ΠΎΡΠΈΠ±ΠΊΠ°ΠΌ;
- ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ° backtrace Π΄Π»Ρ debug;
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
Anyhow ΡΠΎΠ·Π΄Π°ΡΡ Π°Π»ΠΈΠ°Ρ Π½Π°ΠΏΠΎΠ΄ΠΎΠ±ΠΈΠ΅ Result<T> = Result<T, Box<dyn Error>>
, ΡΡΠΎΠ±Ρ ΡΠΊΡΡΡΡ ΡΠΈΠΏ ΠΎΡΠΈΠ±ΠΎΠΊ ΠΈ ΡΠ΄Π΅Π»Π°ΡΡ Π΅Π³ΠΎ ΡΠ½ΠΈΠ²Π΅ΡΡΠ°Π»ΡΠ½ΡΠΌ.
// ---- ΠΠ΅Π· Anyhow
fn string_error() -> Result<(), String> {
Ok(())
}
fn io_error() -> Result<(), std::io::Error> {
Ok(())
}
fn any_error() -> Result<(), Box<dyn Error>> {
string_error()?;
io_error()?;
Ok(())
}
// ---- Π‘ Anyhow:
use anyhow::Result;
fn string_error() -> Result<()> {
Ok(())
}
fn io_error() -> Result<()> {
Ok(())
}
fn any_error() -> Result<()> {
string_error()?;
io_error()?;
Ok(())
}
ΠΡΠΈΠΌΠ΅Ρ Π½Π΅ΡΠ΄Π°ΡΠ½ΠΎΠ³ΠΎ ΡΡΠ΅Π½ΠΈΡ ΡΠ°ΠΉΠ»Π°:
use anyhow::{Context, Result};
fn read_config_file(path: &str) -> Result<String> {
std::fs::read_to_string(path).with_context(|| format!("Failed to read file {}", path))
}
fn main() -> Result<()> {
let config_content = read_config_file("conf.txt")?;
println!("Config content:\n{:?}", config_content);
Ok(())
}
Result<T>
ΡΡΠ°Π½ΠΎΠ²ΠΈΡΡΡ Π°Π»ΠΈΠ°ΡΠΎΠΌ ΠΊ Result<T, anyhow::Error>
;
Context
, with_context()
ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΠΎΡΡΠΈ ΠΊ ΠΎΡΠΈΠ±ΠΊΠ΅, Π² ΡΠ»ΡΡΠ°Π΅ Π½Π΅ΡΡΠΏΠ΅Ρ
Π° ΡΡΠ½ΠΊΡΠΈΠΈ ΡΡΠ΅Π½ΠΈΡ read_to_string()
;
- ΠΠΏΠ΅ΡΠ°ΡΠΎΡ
?
Π²ΡΠ½ΠΎΡΠΈΡ ΠΎΡΠΈΠ±ΠΊΡ Π²Π²Π΅ΡΡ
, ΠΏΡΠΈ ΡΡΠΎΠΌ Π°Π²ΡΠΎ-ΠΊΠΎΠ½Π²Π΅ΡΡΠΈΡΡΠ΅Ρ Π΅Ρ ΡΠΈΠΏ Π² anyhow::Error
.
ΠΠ°ΠΌΠ΅Π½Π° Box<dyn>
Ρ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠΎΠΌ
ΠΠΎΠ·ΡΠΌΡΠΌ ΠΏΡΠΈΠΌΠ΅Ρ, Π² ΠΊΠΎΡΠΎΡΠΎΠΌ ΡΡΠ΅Π½ΠΈΠ΅ ΡΠ°ΠΉΠ»Π° std::fs::read_to_string()
(ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ Π½Π΅ΡΠ΄Π°ΡΠ½ΡΠΌ), Π΄Π°Π»Π΅Π΅ Π΄Π΅ΡΠΈΡΡΠΎΠ²ΠΊΠ° Π΅Π³ΠΎ ΠΊΠΎΠ½ΡΠ΅Π½ΡΠ° Ρ ΠΏΠΎΠΌΠΎΡΡΡ base64 decode()
(ΠΌΠΎΠΆΠ΅Ρ Π½Π΅ ΠΏΠΎΠ»ΡΡΠΈΡΡΡΡ) Π² ΡΠ΅ΠΏΠΎΡΠΊΡ Π±Π°ΠΉΡ, ΠΈΠ· ΠΊΠΎΡΠΎΡΠΎΠΉ ΡΠΎΡΠΌΠΈΡΡΠ΅ΡΡΡ ΡΡΡΠΎΠΊΠ° String::from_utf8()
(ΠΌΠΎΠΆΠ΅Ρ Π½Π΅ ΠΏΠΎΠ»ΡΡΠΈΡΡΡΡ). ΠΡΠ΅ ΡΡΠΈ ΡΡΠΈ ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΡΡ
ΠΎΡΠΈΠ±ΠΊΠΈ ΠΈΠΌΠ΅ΡΡ ΡΠ°Π·Π½ΡΠΉ ΡΠΈΠΏ.
ΠΠ΄ΠΈΠ½ ΡΠΏΠΎΡΠΎΠ± Π²ΡΠ΅ ΡΡΠΈ ΠΈΡ
ΠΏΡΠΈΠ½ΡΡΡ Π½Π° ΠΎΠ΄Π½Ρ ΡΡΠ½ΠΊΡΠΈΡ, ΡΡΠΎ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Box<dyn std::error::Error>>
, ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ Π²ΡΠ΅ 3 ΠΎΡΠΈΠ±ΠΊΠΈ ΠΏΡΠΈΠΌΠ΅Π½ΡΡΡ std::error::Error
.
use base64::{self, engine, Engine};
fn decode() -> Result<(), Box<dyn std::error::Error>> {
let input = std::fs::read_to_string("input")?;
for line in input.lines() {
let bytes = engine::general_purpose::STANDARD.decode(line)?;
println!("{}", String::from_utf8(bytes)?);
}
Ok(())
}
ΠΠΎΠ΄Ρ
ΠΎΠ΄ ΡΠ°Π±ΠΎΡΠΈΠΉ, Π½ΠΎ ΠΏΡΠΈ ΡΡΠ°Π±Π°ΡΡΠ²Π°Π½ΠΈΠΈ ΠΎΠ΄Π½ΠΎΠΉ ΠΈΠ· ΡΡΡΡ
ΠΎΡΠΈΠ±ΠΎΠΊ, ΡΡΠ΄ΠΈΡΡ ΠΎ ΠΏΡΠΎΠΈΡΡ
ΠΎΠΆΠ΄Π΅Π½ΠΈΠΈ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡΠ΄Π΅Ρ Π»ΠΈΡΡ ΠΏΠΎ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ Π²Π½ΡΡΡΠΈ ΠΎΡΠΈΠ±ΠΊΠΈ.
Π ΡΠ»ΡΡΠ°Π΅ ΠΏΡΠΈΠΌΠ΅Π½Π΅Π½ΠΈΡ Anyhow, ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΌΠ΅Π½ΠΈΡΡ ΠΈΠΌ Box<dyn>
, ΠΏΡΠΈ ΡΡΠΎΠΌ ΡΡΠ°Π·Ρ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ½ΡΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ, ΠΊΠΎΡΠΎΡΠΎΠ΅ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ ΠΏΠΎΠ½ΡΡΡ ΠΌΠ΅ΡΡΠΎ:
use anyhow::Context;
use base64::{self, engine, Engine};
fn decode() -> Result<(), anyhow::Error> {
let input = std::fs::read_to_string("input")
.context("Failed to read file")?; // ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡ 1
for line in input.lines() {
let bytes = engine::general_purpose::STANDARD
.decode(line)
.context("Failed to decode the input")?; // ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡ 2
println!(
"{}",
String::from_utf8(bytes)
.context("Failed to cenvert bytes")? // ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡ 3
);
}
Ok(())
clap CLI
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
Π£ΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ Π²ΠΌΠ΅ΡΡΠ΅ Ρ Π΄ΠΎΠΏΠΎΠΌ derive, ΡΡΠΎΠ±Ρ Π΄ΠΎΠ±Π°Π²Π»ΡΡΡ clap ΠΊΠ°ΠΊ ΠΏΡΠΈΠ·Π½Π°ΠΊ (trait) Ρ ΡΡΡΡΠΊΡΡΡ Π΄Π°Π½Π½ΡΡ
.
ΠΠ°ΠΊ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ
ΠΠΎΠ±Π°Π²ΠΊΠ° clap ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΠΏΠ°ΡΡΠΈΡΡ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ, Π° ΡΠ°ΠΊΠΆΠ΅ Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅Ρ ΠΊΠ»ΡΡΠΈ “-h” ΠΈ “–help” Π΄Π»Ρ ΠΏΠΎΠΌΠΎΡΠΈ Π±Π΅Π· ΠΊΠΎΠ΄Π°:
use clap::Parser;
#[derive(Parser, Debug)]
struct Args {
arg1: String,
arg2: usize,
}
fn main() {
let args = Args::parse();
println!("{:?}", args)
}
ΠΡΠΈ Π·Π°ΠΏΡΡΠΊΠ΅ Π΄Π°Π½Π½Π°Ρ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ° ΡΡΠ΅Π±ΡΠ΅Ρ 2 Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠ°, ΠΏΡΠΈΡΠΎΠΌ Π²ΡΠΎΡΠΎΠΉ ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΠΎ ΡΠΈΡΠ»ΠΎΠΌ.
ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠΉ
ΠΠΌΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ ΠΈ Π²Π΅ΡΡΠΈΡ Π²Π½ΠΎΡΡΡΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΌ ΠΏΡΠΈΠ·Π½Π°ΠΊΠΎΠΌ. ΠΠΎΠΏ. ΠΏΠΎΠ»Ρ ΠΎΠΏΠΈΡΠ°Π½ΠΈΡ Π²Π½ΠΎΡΡΡΡΡ Ρ ΠΏΠΎΠΌΠΎΡΡΡ ΡΠΏΠ΅Ρ. ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠ΅Π² ///
:
use clap::Parser;
#[derive(Parser, Debug)]
#[command(author = "Author Name", version, about)]
/// A very simple CLI parser
struct Args {
/// Text argument option
arg1: String,
/// Number argument option
arg2: usize,
}
fn main() {
let args = Args::parse();
println!("{:?}", args)
}
ΠΠΎΠ±Π°Π²ΠΊΠ° ΡΠ»Π°Π³ΠΎΠ²
Π€Π»Π°Π³ΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Π°Π½Π½ΠΎΡΠ°ΡΠΈΠΈ #[arg(short, long)]
Π΄Π»Ρ ΠΊΠΎΡΠΎΡΠΊΠΎΠ³ΠΎ ΠΈ Π΄Π»ΠΈΠ½Π½ΠΎΠ³ΠΎ ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΡ ΡΠ»Π°Π³Π°. ΠΡΠ»ΠΈ Ρ 2-Ρ
ΡΠ»Π°Π³ΠΎΠ² ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²Π°Ρ ΠΏΠ΅ΡΠ²Π°Ρ Π±ΡΠΊΠ²Π°, ΠΌΠΎΠΆΠ½ΠΎ ΡΠΊΠ°Π·Π°ΡΡ Π²ΡΡΡΠ½ΡΡ ΠΈΡ
ΠΊΠΎΡΠΎΡΠΊΡΡ Π²Π΅ΡΡΠΈΡ. ΠΠΎΡΠΎΡΠΊΠ°Ρ Π²Π΅ΡΡΠΈΡ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ String, ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ»ΡΠΊΠΎ 1 char.
<..>
struct Args {
#[arg(short = 'a', long)]
/// Text argument option
arg1: String,
#[arg(short = 'A', long)]
/// Number argument option
arg2: usize,
}
<..>
ΠΠ΅ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΡΠ΅ ΡΠ»Π°Π³ΠΈ
ΠΠ»Ρ ΠΎΡΠΌΠ΅ΡΠΊΠΈ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠ° ΠΊΠ°ΠΊ Π½Π΅ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΠΎΠ³ΠΎ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΡΠΊΠ°Π·Π°ΡΡ Π΅Π³ΠΎ ΡΠΈΠΏ ΠΊΠ°ΠΊ Option<ΡΠΈΠΏ>
ΠΈ Π² ΡΠΊΠΎΠ±ΠΊΠ°Ρ
ΠΈΡΡ
ΠΎΠ΄Π½ΡΠΉ ΡΠΈΠΏ Π΄Π°Π½Π½ΡΡ
:
struct Args {
#[arg(short = 'a', long)]
/// Text argument option
arg1: String,
#[arg(short = 'A', long)]
/// Number argument option
arg2: Option<usize>,
}
Π’Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ
ΠΎΠ΄ ΠΏΠΎΡΡΠ΅Π±ΡΠ΅Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠ°ΡΡ ΡΠΈΡΡΠ°ΡΠΈΡ, ΠΊΠΎΠ³Π΄Π° Π² arg2 Π½ΠΈΡΠ΅Π³ΠΎ Π½Π΅Ρ. ΠΠΌΠ΅ΡΡΠΎ ΡΠ°ΠΊ Π΄Π΅Π»Π°ΡΡ, ΠΌΠΎΠΆΠ½ΠΎ ΡΠΊΠ°Π·Π°ΡΡ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ:
struct Args {
#[arg(short = 'a', long)]
/// Text argument option
arg1: String,
#[arg(default_value_t=usize::MAX, short = 'A', long)]
/// Number argument option
arg2: usize,
}
Π’Π΅ΠΏΠ΅ΡΡ arg2 ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ Π±ΡΠ΄Π΅Ρ ΡΠ°Π²Π΅Π½ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠΌΡ ΡΠΈΡΠ»Ρ usize
, Π΅ΡΠ»ΠΈ Π½Π΅ ΡΠΊΠ°Π·Π°Π½ΠΎ ΠΈΠ½ΠΎΠ΅.
ΠΠ°Π»ΠΈΠ΄Π°ΡΠΈΡ Π²Π²Π΅Π΄ΡΠ½Π½ΡΡ
Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ
Π ΡΠ»ΡΡΠ°Π΅ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠ°-ΡΡΡΠΎΠΊΠΈ Π΅ΡΡΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡ Π²Π²Π΅ΡΡΠΈ ΠΏΡΡΡΡΡ ΡΡΡΠΎΠΊΡ ΠΈΠ· ΠΏΡΠΎΠ±Π΅Π»ΠΎΠ² " "
. ΠΠ»Ρ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ ΡΠ°ΠΊΠΈΡ
Π²Π°ΡΠΈΠ°Π½ΡΠΎΠ², Π²Π²ΠΎΠ΄ΠΈΡΡΡ ΡΡΠ½ΠΊΡΠΈΡ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ ΠΈ Π΅Ρ Π²ΡΠ·ΠΎΠ²:
use clap::Parser;
#[derive(Parser, Debug)]
#[command(author = "Author Name", version, about)]
/// A very simple CLI parser
struct Args {
#[arg(value_parser = validate_argument_name, short = 'a', long)]
/// Text argument option
arg1: String,
#[arg(default_value_t=usize::MAX, short = 'A', long)]
/// Number argument option
arg2: usize,
}
fn validate_argument_name(name: &str) -> Result<String, String> {
if name.trim().len() != name.len() {
Err(String::from(
"ΡΡΡΠΎΠΊΠ° Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Π° Π½Π°ΡΠΈΠ½Π°ΡΡΡΡ ΠΈΠ»ΠΈ Π·Π°ΠΊΠ°Π½ΡΠΈΠ²Π°ΡΡΡΡ ΠΏΡΠΎΠ±Π΅Π»Π°ΠΌΠΈ",
))
} else {
Ok(name.to_string())
}
}
fn main() {
let args = Args::parse();
println!("{:?}", args)
}
Π’Π΅ΠΏΠ΅ΡΡ ΠΏΡΠΈ ΠΏΠΎΠΏΡΡΠΊΠ΅ Π²ΡΠ·Π²Π°ΡΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ tiny-clapper -- -a " "
Π±ΡΠ΄Π΅Ρ ΠΏΠΎΠΊΠ°Π·Π°Π½Π° ΠΎΡΠΈΠ±ΠΊΠ° Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ.
Command CLI
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π·Π°ΠΏΡΡΠΊΠ°ΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ Π² ΠΠ‘.
ΠΠ°ΡΠΈΠ°Π½ΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ
spawn
β runs the program and returns a value with details
output
β runs the program and returns the output
status
β runs the program and returns the exit code
ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
Output - ΠΏΡΠΎΡΡΠΎ ΠΈ Π±ΡΡΡΡΠΎ
let output = Command::new("cat")
.arg("/etc/issue")
.output()
.with_context(|| "ls command failed")?;
println!("{}", output.status);
println!("{}", String::from_utf8_lossy(&output.stdout));
println!("{}", String::from_utf8_lossy(&output.stderr));
βΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠ΅ - ΠΌΠΎΠΆΠ½ΠΎ Π²ΡΠ·ΡΠ²Π°ΡΡ ΡΠΎΠ»ΡΠΊΠΎ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΡ, Π½Π΅Π»ΡΠ·Ρ Π΄ΠΎΠ±Π°Π²Π»ΡΡΡ ΡΠ²ΠΎΠΉ ΡΠ΅ΠΊΡΡ.
spawn - Π³ΠΈΠ±ΠΊΠΈΠΉ Π²Π²ΠΎΠ΄
Π‘Π°ΠΌΡΠΉ Π³ΠΈΠ±ΠΊΠΈΠΉ Π²Π°ΡΠΈΠ°Π½Ρ, ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡΡΠΈΠΉ Π΄Π΅Π»Π°ΡΡ ΡΠ²ΠΎΠΉ Π²Π²ΠΎΠ΄, Π° Π½Π΅ ΡΠΎΠ»ΡΠΊΠΎ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠ΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΠΈ ΡΠ°ΠΉΠ»Ρ-ΠΏΠ°ΠΏΠΊΠΈ, ΡΡΠΎ ΡΠ΅ΡΠ΅Π· spawn
:
let mut child = Command::new("cat") // ΠΊΠΎΠΌΠ°Π½Π΄Π°
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
let stdin = child.stdin.as_mut()?;
stdin.write_all(b"Hello Rust!\n")?; // ΡΠ΅ΠΊΡΡ ΠΊ ΠΊΠΎΠΌΠ°Π½Π΄Π΅, /n ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»Π΅Π½
let output = child.wait_with_output()?;
for i in output.stdout.iter() { // ΡΠΈΠΊΠ» Π½Π° ΡΠ»ΡΡΠ°ΠΉ ΠΌΠ½ΠΎΠ³ΠΎΡΡΡΠΎΡΠ½ΠΎΠ³ΠΎ Π²ΡΠ²ΠΎΠ΄Π°
print!("{}", *i as char);
}
Ok(())
βΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠ΅ - ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ΄Π°Π²Π°ΡΡ Π½Π° Π²Ρ
ΠΎΠ΄ ΡΠ΅ΠΊΡΡ Π»ΠΈΡΡ ΡΠ΅ΠΌ ΠΊΠΎΠΌΠ°Π½Π΄Π°ΠΌ, ΠΊΠΎΡΠΎΡΡΠ΅ ΡΡΠ΅Π±ΡΡΡ ΡΡΠ°Π·Ρ ΡΠΊΠ°Π·Π°ΡΡ Π²Π²ΠΎΠ΄Π½ΡΠΉ ΡΠ΅ΠΊΡΡ. ΠΡΠΈ ΡΡΠΎΠΌ ΡΡΠ΄ ΠΊΠΎΠΌΠ°Π½Π΄ Π΄Π΅Π»Π°ΡΡ ΠΏΠ°ΡΠ·Ρ ΠΏΠ΅ΡΠ΅Π΄ ΠΏΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΠ΅ΠΌ ΡΠ΅ΠΊΡΡΠ° Π½Π° Π²Ρ
ΠΎΠ΄, Ρ ΡΠ°ΠΊΠΈΠΌΠΈ ΡΠ²ΠΎΠΉ Π²Π²ΠΎΠ΄ ΡΠ°Π±ΠΎΡΠ°ΡΡ Π½Π΅ Π±ΡΠ΄Π΅Ρ ΡΡΠΎ ΠΎΡΠ½ΠΎΡΠΈΡΡΡ ΠΈ ΠΊ ΡΠΈΠ»ΡΡΡΠ°ΡΠΈΠΈ ΡΠ΅ΡΠ΅Π· pipe = | grep <...>
ΠΈ Π°Π½Π°Π»ΠΎΠ³ΠΈ.
Pipe (nightly) - ΠΏΠΎΠ»Π½ΡΠΉ Π²Π²ΠΎΠ΄ (Π½Π΅ ΠΏΡΠΎΠ²Π΅ΡΠ΅Π½Π½ΡΠΉ ΡΠΏΠΎΡΠΎΠ±)
https://doc.rust-lang.org/std/pipe/fn.pipe.html
ΠΠ»Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ Π½ΡΠΆΠ΅Π½ nightly Π²Π°ΡΠΈΠ°Π½Ρ Rust
rustup install nightly
rustup default nightly
ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅:
#![feature(anonymous_pipe)] // ΡΠΎΠ»ΡΠΊΠΎ Π² Rust Nightly
use std::pipe
let text = "| grep file".as_bytes();
// ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ ΡΠ°ΠΌΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ
let child = Command::new("ls")
.arg("/Users/test")
.stdin({
// ΠΠ΅Π»ΡΠ·Ρ ΠΎΡΠΏΡΠ°Π²ΠΈΡΡ ΠΏΡΠΎΡΡΠΎ ΡΡΡΠΎΠΊΡ Π² ΠΊΠΎΠΌΠ°Π½Π΄Ρ
// ΠΡΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°ΡΡ ΡΠ°ΠΉΠ»ΠΎΠ²ΡΠΉ Π΄Π΅ΡΠΊΡΠΈΠΏΡΠΎΡ (ΠΊΠ°ΠΊ Π² ΠΎΠ±ΡΡΠ½ΠΎΠΌ stdin "pipe")
// ΠΠΎΡΡΠΎΠΌΡ ΡΠΎΠ·Π΄Π°ΡΠΌ ΠΏΠ°ΡΡ pipes ΡΡΡ
let (reader, mut writer) = std::pipe::pipe().unwrap();
// ΠΠΈΡΠ΅ΠΌ ΡΡΡΠΎΠΊΡ Π² ΠΎΠ΄Π½Ρ pipe
writer.write_all(text).unwrap();
// Π΄Π°Π»Π΅Π΅ ΠΏΡΠ΅Π²ΡΠ°ΡΠ°Π΅ΠΌ Π²ΡΠΎΡΡΡ Π΄Π»Ρ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠΈ Π² ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΡΡΠ°Π·Ρ ΠΏΡΠΈ spawn.
Stdio::from(reader)
})
.spawn()?;
External link: https://docs.rs/itertools/latest/itertools/
Working with iterators
Sorting() a string of letters (with rev() - reverse order)
use itertools::Itertools;
let text = "Hello world";
let text_sorted = text.chars().sorted().rev().collect::<String>();
// rev() - Iterate the iterable in reverse
println!("Text: {}, Sorted Text: {}", text, text_sorted);
// Text: Hello world, Sorted Text: wroollledH
Counts() ΠΏΠΎΠ΄ΡΡΡΡ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Π° ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²ΡΡ
ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ² Π² Array
use itertools::Itertools;
let number_list = [1,12,3,1,5,2,7,8,7,8,2,3,12,7,7];
let mode = number_list.iter().counts(); // Itertools::counts()
// Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ Hashmap, Π³Π΄Π΅ ΠΊΠ»ΡΡΠΈ Π²Π·ΡΡΡ ΠΈΠ· ΠΌΠ°ΡΡΠΈΠ²Π°, Π·Π½Π°ΡΠ΅Π½ΠΈΡ - ΡΠ°ΡΡΠΎΡΠ°
for (key, value) in &mode {
println!("Π§ΠΈΡΠ»ΠΎ {key} Π²ΡΡΡΠ΅ΡΠ°Π΅ΡΡΡ {value} ΡΠ°Π·");
}
rand
Generating random numbers
External links:
Using rand lib example:
use rand::Rng;
fn main() {
let secret_of_type = rand::rng().random::<u32>();
let secret = rand::rng().random_range(1..=100);
println!("Random nuber of type u32: {secret_of_type}");
println!("Random nuber from 1 to 100: {}", secret); }
Π ΡΡΠ°ΡΠΎΠΉ Π²Π΅ΡΡΠΈΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ ΠΏΡΠΈΠΌΠ΅Π½ΡΠ»ΡΡ ΠΏΡΠΈΠ·Π½Π°ΠΊ gen(), ΠΊΠΎΡΠΎΡΡΠΉ ΠΏΠ΅ΡΠ΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π»ΠΈ Π² ΡΠ²ΡΠ·ΠΈ Ρ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ gen() Π² Rust 2024.
regex
ΠΠ½Π΅ΡΠ½ΠΈΠ΅ ΡΡΡΠ»ΠΊΠΈ:
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
REGEX ΠΡΡΠ°ΠΆΠ΅Π½ΠΈΡ
ΠΠ΄ΠΈΠ½ ΡΠΈΠΌΠ²ΠΎΠ»
. any character except new line (includes new line with s flag)
\d digit (\p{Nd})
\D not digit
\pX Unicode character class identified by a one-letter name
\p{Greek} Unicode character class (general category or script)
\PX Negated Unicode character class identified by a one-letter name
\P{Greek} negated Unicode character class (general category or script)
ΠΠ»Π°ΡΡΡ ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ²
[xyz] A character class matching either x, y or z (union).
[^xyz] A character class matching any character except x, y and z.
[a-z] A character class matching any character in range a-z.
[[:alpha:]] ASCII character class ([A-Za-z])
[[:^alpha:]] Negated ASCII character class ([^A-Za-z])
[x[^xyz]] Nested/grouping character class (matching any character except y and z)
[a-y&&xyz] Intersection (matching x or y)
[0-9&&[^4]] Subtraction using intersection and negation (matching 0-9 except 4)
[0-9--4] Direct subtraction (matching 0-9 except 4)
[a-g~~b-h] Symmetric difference (matching `a` and `h` only)
[\[\]] Escaping in character classes (matching [ or ])
Π‘ΠΎΠ²ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ²
xy concatenation (x followed by y)
x|y alternation (x or y, prefer x)
ΠΠΎΠ²ΡΠΎΡΡ ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ²
x* zero or more of x (greedy)
x+ one or more of x (greedy)
x? zero or one of x (greedy)
x*? zero or more of x (ungreedy/lazy)
x+? one or more of x (ungreedy/lazy)
x?? zero or one of x (ungreedy/lazy)
x{n,m} at least n x and at most m x (greedy)
x{n,} at least n x (greedy)
x{n} exactly n x
x{n,m}? at least n x and at most m x (ungreedy/lazy)
x{n,}? at least n x (ungreedy/lazy)
x{n}? exactly n x
ΠΡΡΡΡΠ΅ ΡΠΈΠΌΠ²ΠΎΠ»Ρ
^ the beginning of text (or start-of-line with multi-line mode)
$ the end of text (or end-of-line with multi-line mode)
\A only the beginning of text (even with multi-line mode enabled)
\z only the end of text (even with multi-line mode enabled)
\b a Unicode word boundary (\w on one side and \W, \A, or \z on other)
\B not a Unicode word boundary
ΠΡΡΠΏΠΏΠΈΡΠΎΠ²ΠΊΠ° ΠΈ ΡΠ»Π°Π³ΠΈ
(exp) numbered capture group (indexed by opening parenthesis)
(?P<name>exp) named (also numbered) capture group (names must be alpha-numeric)
(?<name>exp) named (also numbered) capture group (names must be alpha-numeric)
(?:exp) non-capturing group
(?flags) set flags within current group
(?flags:exp) set flags for exp (non-capturing)
Π‘ΠΏΠ΅Ρ-ΡΠΈΠΌΠ²ΠΎΠ»Ρ
\* literal *, works for any punctuation character: \.+*?()|[]{}^$
\a bell (\x07)
\f form feed (\x0C)
\t horizontal tab
\n new line
\r carriage return
\v vertical tab (\x0B)
\123 octal character code (up to three digits) (when enabled)
\x7F hex character code (exactly two digits)
\x{10FFFF} any hex character code corresponding to a Unicode code point
\u007F hex character code (exactly four digits)
\u{7F} any hex character code corresponding to a Unicode code point
\U0000007F hex character code (exactly eight digits)
\U{7F} any hex character code corresponding to a Unicode code point
Π’ΠΈΠΏΡ ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ² ΠΏΠΎ ASCII
[[:alnum:]] alphanumeric ([0-9A-Za-z])
[[:alpha:]] alphabetic ([A-Za-z])
[[:ascii:]] ASCII ([\x00-\x7F])
[[:blank:]] blank ([\t ])
[[:cntrl:]] control ([\x00-\x1F\x7F])
[[:digit:]] digits ([0-9])
[[:graph:]] graphical ([!-~])
[[:lower:]] lower case ([a-z])
[[:print:]] printable ([ -~])
[[:punct:]] punctuation ([!-/:-@\[-`{-~])
[[:space:]] whitespace ([\t\n\v\f\r ])
[[:upper:]] upper case ([A-Z])
[[:word:]] word characters ([0-9A-Za-z_])
[[:xdigit:]] hex digit ([0-9A-Fa-f])
ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ REGEX
Π‘Π²Π΅ΡΠΊΠ° Ρ is_match()
use regex::Regex;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let input = "hello, world!";
let pattern = Regex::new(r"hello, (world|universe)!")?;
let is_match = pattern.is_match(input);
println!("Is match: {}", is_match); // true
Ok(())
}
ΠΠ»Ρ ΠΏΡΠΎΡΡΠΎΠ³ΠΎ Π²ΡΡΡΠ½Π΅Π½ΠΈΡ ΡΠ°ΠΊΡΠ° ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΡ Π»ΡΡΡΠ΅ Π²ΡΠ΅Π³ΠΎ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡΡΡ is_match
ΠΊΠ°ΠΊ ΡΠ°ΠΌΡΠΌ Π±ΡΡΡΡΡΠΌ ΡΠΏΠΎΡΠΎΠ±ΠΎΠΌ.
ΠΠΎΠΈΡΠΊ ΠΏΠ΅ΡΠ²ΠΎΠ³ΠΎ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΡ
let pattern = regex::Regex::new(r"hello, (world|universe)!")?;
let input = "hello, world!";
let first_match = pattern.find(input);
println!("First match: {}", first_match.unwrap().as_str());
ΠΠ΅ΡΠ²ΠΎΠ΅ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠ΅ Π±ΡΠ΄Π΅Ρ ΠΈΠΌΠ΅ΡΡ ΡΠΈΠΏ Option<match>
, Π° Π² ΡΠ»ΡΡΠ°Π΅ ΠΎΡΡΡΡΡΡΠ²ΠΈΡ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠΉ = None
.
ΠΠΎΠΈΡΠΊ Π²ΡΠ΅Ρ
ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠΉ
let pattern = regex::Regex::new(r"hello, (world|universe)!")?;
let input = "hello, world! hello, universe!";
let matches: Vec<_> = pattern.find_iter(input).collect(); // find_iter()
matches.iter().for_each(|i| println!("{}", i.as_str()));
// matches = Vec<match> ΠΈ ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ Π²ΡΠ΅ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΡ
serde
Installing
Add serde framework with Derive feature to use it in structures and functions. Also add a separate serde_json lib for converting into specifically JSON:
cargo add serde -F derive
cargo add serde_json
Usage
Add serde, then mark the structures with Serialise, Deserialise traits and use serde_json for serialising:
use serde::{Deserialize, Serialize};
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub enum LoginRole {
Admin,
User,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub username: String,
pub password: String,
pub role: LoginRole,
}
pub fn get_default_users() -> HashMap<String, User> {
let mut users = HashMap::new();
users.insert(
"admin".to_string(),
User::new("admin", "password", LoginRole::Admin),
);
users.insert(
"bob".to_string(),
User::new("bob", "password", LoginRole::User),
);
users
}
pub fn get_users() -> HashMap<String, User> {
let users_path = Path::new("users.json");
if users_path.exists() {
// Load the file!
let users_json = std::fs::read_to_string(users_path).unwrap();
let users: HashMap<String, User> = serde_json::from_str(&users_json).unwrap();
users
} else {
// Create a file and return it
let users = get_default_users();
let users_json = serde_json::to_string(&users).unwrap();
std::fs::write(users_path, users_json).unwrap();
users
}
sysinfo
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π΄Π»Ρ ΠΈΠ·ΡΡΠ΅Π½ΠΈΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ² ΡΠΈΡΡΠ΅ΠΌΡ ΠΈ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΡ
ΡΠ΅ΡΡΡΡΠΎΠ².
https://docs.rs/sysinfo/latest/sysinfo/
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
use sysinfo::{RefreshKind, System};
fn main() {
let _sys = System::new_with_specifics(RefreshKind::nothing());
println!("System name: {:?}", System::name());
println!("System kernel version: {:?}", System::kernel_version());
println!("System OS version: {:?}", System::os_version());
println!("System host name: {:?}", System::host_name());
}
Π‘ ΠΏΠΎΠΌΠΎΡΡΡ RefreshKind::nothing()
ΠΎΡΠΊΠ»ΡΡΠ°ΡΡΡΡ Π²ΡΠ΅ Π΄ΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΈΠ΅ Π²Π΅ΡΠΈ, ΡΠ°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ ΠΎΡΡΠ°ΡΠΎΠΊ ΡΠ²ΠΎΠ±ΠΎΠ΄Π½ΠΎΠΉ ΠΏΠ°ΠΌΡΡΠΈ, Π·Π°Π³ΡΡΠ·ΠΊΠ° Π¦ΠΠ£, ΡΠ΅ΡΡ ΠΈ ΡΠ°ΠΊ Π΄Π°Π»Π΅Π΅, ΡΡΠΎ Π΄ΡΠ°ΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΡΡΠΊΠΎΡΡΠ΅Ρ ΠΎΠΏΡΠΎΡ ΡΠΈΡΡΠ΅ΠΌΡ.
ΠΠΎΠΆΠ½ΠΎ ΡΠ°ΡΡΠΈΡΠ½ΠΎ ΠΎΠΏΡΠ°ΡΠΈΠ²Π°ΡΡ ΡΠ°Π·Π½ΡΠ΅ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠ΅ ΡΠ΅ΡΡΡΡΡ, ΠΏΠΎ Π·Π°Π΄Π°Π½Π½ΡΠΌ ΠΈΠ½ΡΠ΅ΡΠ²Π°Π»Π°ΠΌ.
Tokio tracing
ΠΠ½Π΅ΡΠ½ΠΈΠ΅ ΡΡΡΠ»ΠΊΠΈ:
ΠΠ°Π±Π»ΡΠ΄Π°Π΅ΠΌΠΎΡΡΡ / Observability
ΠΠ°Π±Π»ΡΠ΄Π°Π΅ΠΌΠΎΡΡΡ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΠΏΠΎΠ½ΡΡΡ ΡΠΈΡΡΠ΅ΠΌΡ ΠΈΠ·Π²Π½Π΅, Π·Π°Π΄Π°Π²Π°Ρ Π²ΠΎΠΏΡΠΎΡΡ ΠΎ Π½Π΅ΠΉ, ΠΏΡΠΈ ΡΡΠΎΠΌ Π½Π΅ Π·Π½Π°Ρ Π΅Π΅ Π²Π½ΡΡΡΠ΅Π½Π½Π΅Π³ΠΎ ΡΡΡΡΠΎΠΉΡΡΠ²Π°. ΠΡΠΎΠΌΠ΅ ΡΠΎΠ³ΠΎ, ΠΎΠ½Π° ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π»Π΅Π³ΠΊΠΎ ΡΡΡΡΠ°Π½ΡΡΡ Π½Π΅ΠΏΠΎΠ»Π°Π΄ΠΊΠΈ ΠΈ ΡΠ΅ΡΠ°ΡΡ Π½ΠΎΠ²ΡΠ΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ, ΡΠΎ Π΅ΡΡΡ Β«Π½Π΅ΠΈΠ·Π²Π΅ΡΡΠ½ΡΠ΅ Π½Π΅ΠΈΠ·Π²Π΅ΡΡΠ½ΡΠ΅Β». ΠΠ½Π° ΡΠ°ΠΊΠΆΠ΅ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ Π²Π°ΠΌ ΠΎΡΠ²Π΅ΡΠΈΡΡ Π½Π° Π²ΠΎΠΏΡΠΎΡ Β«ΠΏΠΎΡΠ΅ΠΌΡ ΡΡΠΎ-ΡΠΎ ΠΏΡΠΎΠΈΡΡ
ΠΎΠ΄ΠΈΡ?Β».
Π§ΡΠΎΠ±Ρ Π·Π°Π΄Π°ΡΡ ΡΡΠΈ Π²ΠΎΠΏΡΠΎΡΡ ΠΎ Π²Π°ΡΠ΅ΠΉ ΡΠΈΡΡΠ΅ΠΌΠ΅, ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΡ ΠΏΠΎΠ»Π½ΡΠΉ Π½Π°Π±ΠΎΡ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠΎΠ². Π’ΠΎ Π΅ΡΡΡ ΠΊΠΎΠ΄ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΎΡΠΏΡΠ°Π²Π»ΡΡΡ ΡΠΈΠ³Π½Π°Π»Ρ, ΡΠ°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΠΊΠΈ, ΠΌΠ΅ΡΡΠΈΠΊΠΈ ΠΈ ΠΆΡΡΠ½Π°Π»Ρ. ΠΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠΈΡΠΎΠ²Π°Π½ΠΎ, ΠΊΠΎΠ³Π΄Π° ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊΠ°ΠΌ Π½Π΅ Π½ΡΠΆΠ½ΠΎ Π΄ΠΎΠ±Π°Π²Π»ΡΡΡ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ Π΄Π»Ρ ΡΡΡΡΠ°Π½Π΅Π½ΠΈΡ Π½Π΅ΠΏΠΎΠ»Π°Π΄ΠΎΠΊ, ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ Ρ Π½ΠΈΡ
Π΅ΡΡΡ Π²ΡΡ Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠ°Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ.
Π’Π΅ΡΠΌΠΈΠ½ΠΎΠ»ΠΎΠ³ΠΈΡ
- Π‘ΠΎΠ±ΡΡΠΈΠ΅ ΠΆΡΡΠ½Π°Π»Π° (Log event/message) - ΡΠΎΠ±ΡΡΠΈΠ΅, ΠΏΡΠΎΠΈΠ·ΠΎΡΠ΅Π΄ΡΠ΅Π΅ Π² ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ;
- ΠΡΠΎΠΌΠ΅ΠΆΡΡΠΎΠΊ (Span record) - Π·Π°ΠΏΠΈΡΡ ΠΏΠΎΡΠΎΠΊΠ° ΠΈΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ Π² ΡΠΈΡΡΠ΅ΠΌΠ΅ Π·Π° ΠΏΠ΅ΡΠΈΠΎΠ΄ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ. ΠΠ½ ΡΠ°ΠΊΠΆΠ΅ Π²ΡΠΏΠΎΠ»Π½ΡΠ΅Ρ ΡΡΠ½ΠΊΡΠΈΠΈ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ° Π΄Π»Ρ ΡΠΎΠ±ΡΡΠΈΠΉ ΠΆΡΡΠ½Π°Π»Π° ΠΈ ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ Π΄Π»Ρ ΠΏΠΎΠ΄-ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΊΠΎΠ²;
- Π’ΡΠ°ΡΡΠ° (trace) - ΠΏΠΎΠ»Π½Π°Ρ Π·Π°ΠΏΠΈΡΡ ΠΏΠΎΡΠΎΠΊΠ° ΠΈΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ Π² ΡΠΈΡΡΠ΅ΠΌΠ΅ ΠΎΡ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ Π·Π°ΠΏΡΠΎΡΠ° Π΄ΠΎ ΠΎΡΠΏΡΠ°Π²ΠΊΠΈ ΠΎΡΠ²Π΅ΡΠ°. ΠΡΠΎ ΠΏΠΎ ΡΡΡΠΈ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΠΊ-ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ, ΠΈΠ»ΠΈ ΠΊΠΎΡΠ½Π΅Π²ΠΎΠΉ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΠΊ;
- ΠΠΎΠ΄ΠΏΠΈΡΡΠΈΠΊ (subscriber) - ΡΠ΅Π°Π»ΠΈΠ·ΡΠ΅Ρ ΡΠΏΠΎΡΠΎΠ± ΡΠ±ΠΎΡΠ° Π΄Π°Π½Π½ΡΡ
ΡΡΠ°ΡΡΡ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, Π·Π°ΠΏΠΈΡΡ ΠΈΡ
Π² ΡΡΠ°Π½Π΄Π°ΡΡΠ½ΡΠΉ Π²ΡΠ²ΠΎΠ΄;
- ΠΠΎΠ½ΡΠ΅ΠΊΡΡ ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΠΊΠΈ (Tracing Context): Π½Π°Π±ΠΎΡ Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ, ΠΊΠΎΡΠΎΡΡΠ΅ Π±ΡΠ΄ΡΡ ΠΏΠ΅ΡΠ΅Π΄Π°Π²Π°ΡΡΡΡ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠ»ΡΠΆΠ±Π°ΠΌΠΈ
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
cargo add tracing
cargo add tracing-subscriber -F json
ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ
ΠΡΠΎΡΡΠ°Ρ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΈ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ ΡΠΎΠ±ΡΡΠΈΠΉ:
use tracing::info;
fn main() {
// Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Π³Π»ΠΎΠ±Π°Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠ±ΠΎΡΡΠΈΠΊΠ° ΠΏΠΎ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ
tracing_subscriber::fmt::init();
let number_of_yaks = 3;
// Π½ΠΎΠ²ΠΎΠ΅ ΡΠΎΠ±ΡΡΠΈΠ΅, Π²Π½Π΅ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΊΠΎΠ²
info!(number_of_yaks, "preparing to shave yaks");
}
Π ΡΡΠ½Π°Ρ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΡΠ²ΠΎΠΉΡΡΠ² ΠΏΠΎΠ΄ΠΏΠΈΡΡΠΈΠΊΠ° Π΄Π»Ρ ΡΠΎΡΠΌΠ°ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π»ΠΎΠ³Π°:
fn setup_tracing() {
let subscriber = tracing_subscriber::fmt()
.json() // Π½ΡΠΆΠ½ΠΎ cargo add tracing-subscriber -F json
.with_max_level(tracing::Level::TRACE) // ΠΠΠ₯ ΡΡΠΎΠ²Π΅Π½Ρ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΡ
.compact() // ΠΊΠΎΠΌΠΏΠ°ΠΊΡΠ½ΡΠΉ Π»ΠΎΠ³
.with_file(true) // ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°ΡΡ ΡΠ°ΠΉΠ»-ΠΈΡΡ
ΠΎΠ΄Π½ΠΈΠΊ
.with_line_number(true) // ΠΏΠΎΠΊΠ°Π·Π°ΡΡ Π½ΠΎΠΌΠ΅ΡΠ° ΡΡΡΠΎΠΊ ΠΊΠΎΠ΄Π°
.with_thread_ids(true) // ΠΏΠΎΠΊΠ°Π·Π°ΡΡ ID ΠΏΠΎΡΠΎΠΊΠ° Ρ ΡΠΎΠ±ΡΡΠΈΠ΅ΠΌ
.with_target(false) // Π½Π΅ ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°ΡΡ ΡΠ΅Π»Ρ (ΠΌΠΎΠ΄ΡΠ»Ρ) ΡΠΎΠ±ΡΡΠΈΡ
.finish();
tracing::subscriber::set_global_default(subscriber).unwrap();
tracing::info!("Starting up");
tracing::warn!("Are you sure this is a good idea?");
tracing::error!("This is an error!");
}
Π’ΡΠ°ΡΡΠΈΡΠΎΠ²ΠΊΠ° Π² ΡΠΈΠ½Ρ
ΡΠΎΠ½Π½ΠΎΠΌ ΡΠ΅ΠΆΠΈΠΌΠ΅
use tracing::{instrument, warn};
#[instrument]
fn sync_tracing() {
warn!("event 1");
sync_tracing_sub();
}
#[instrument]
fn sync_tracing_sub() {
warn!("event 2");
}
fn main() {
setup_tracing();
sync_tracing();
}
ΠΠ°ΠΊΡΠΎΡ #[instrument]
Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΡΠΎΠ·Π΄Π°ΡΡ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΊΠΈ (spans) Π΄Π»Ρ ΡΡΠ½ΠΊΡΠΈΠΉ, Π° ΠΏΠΎΠ΄ΠΏΠΈΡΡΠΈΠΊ (subscriber) Π½Π°ΡΡΡΠΎΠ΅Π½ Π²ΡΠ²ΠΎΠ΄ΠΈΡΡ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΊΠΈ Π² stdout.
Π’ΡΠ°ΡΡΠΈΡΠΎΠ²ΠΊΠ° ΠΏΠΎΡΠΎΠΊΠΎΠ² Π² Π°ΡΠΈΠ½Ρ
ΡΠΎΠ½Π½ΠΎΠΌ ΡΠ΅ΠΆΠΈΠΌΠ΅
use tracing_subscriber::fmt::format::FmtSpan;
#[tracing::instrument] // ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ ΡΠ»Π΅Π΄ΠΈΡ Π·Π° Π²ΡΠ΅ΠΌΠ΅Π½Π΅ΠΌ ΡΠ°Π±ΠΎΡΡ
async fn hello() {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
#[tokio::main] // cargo add tokio -F time,macros,rt-multi-thread
async fn main() -> anyhow::Result<()> {
let subscriber = tracing_subscriber::fmt()
.json()
.compact()
.with_file(true)
.with_line_number(true)
.with_thread_ids(true)
.with_target(false)
.with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE) // Π²Ρ
ΠΎΠ΄ ΠΈ Π²ΡΡ
ΠΎΠ΄
.finish(); // ΠΏΠΎΡΠΎΠΊΠ° ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°ΡΡ
tracing::subscriber::set_global_default(subscriber).unwrap();
hello().await;
Ok(())
}
Π ΠΈΡΠΎΠ³Π΅ ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ Π»ΠΎΠ³ ΡΠ°Π±ΠΎΡΡ ΠΏΠΎΡΠΎΠΊΠ° Ρ Π²ΡΠ΅ΠΌΠ΅Π½Π΅ΠΌ Π·Π°Π΄Π΅ΡΠΆΠΊΠΈ:
2024-12-24T14:30:17.378906Z INFO ThreadId(01) hello: src/main.rs:3: {"message":"enter"} {}
2024-12-24T14:30:18.383596Z INFO ThreadId(01) hello: src/main.rs:3: {"message":"enter"} {}
2024-12-24T14:30:18.383653Z INFO ThreadId(01) hello: src/main.rs:3: {"message":"enter"} {}
2024-12-24T14:30:18.383675Z INFO ThreadId(01) hello: src/main.rs:3: {"message":"close","time.busy":"179Β΅s","time.idle":"1.00s"} {}
ΠΠ°ΠΏΠΈΡΡ ΠΆΡΡΠ½Π°Π»Π° Π² Ρ
ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅
ΠΠ½Π΅ΡΠ½ΠΈΠ΅ ΡΡΡΠ»ΠΊΠΈ:
ΠΠ»Ρ Π·Π°ΠΏΠΈΡΠΈ ΠΆΡΡΠ½Π°Π»Π° ΠΏΡΠΈΠΌΠ΅Π½ΡΠ΅ΡΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΉ ΠΌΠΎΠ΄ΡΠ»Ρ tracing-appender
:
cargo add tracing-appender
Π£ Π½Π΅Π³ΠΎ ΠΌΠ½ΠΎΠ³ΠΎ ΡΡΠ½ΠΊΡΠΈΠΉ Π½Π΅ ΡΠΎΠ»ΡΠΊΠΎ Π·Π°ΠΏΠΈΡΠΈ Π² ΠΎΠ±Π»Π°ΡΠ½ΡΠ΅ ΡΠ»ΡΠΆΠ±Ρ ΡΠΈΠΏΠ° Datadog, Π½ΠΎ ΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΆΡΡΠ½Π°Π»Π° Ρ Π΄ΠΎΠ·Π°ΠΏΠΈΡΡΡ (ΠΌΠΈΠ½ΡΡΠ½ΠΎΠΉ, ΡΠ°ΡΠΎΠ²ΠΎΠΉ, Π΄Π½Π΅Π²Π½ΠΎΠΉ), Π° ΡΠ°ΠΊΠΆΠ΅ Π·Π°ΠΏΠΈΡΡ ΠΊΠ°ΠΊ Π½Π΅Π±Π»ΠΎΠΊΠΈΡΡΡΡΠ΅Π΅ Π΄Π΅ΠΉΡΡΠ²ΠΈΠ΅ Π²ΠΎ Π²ΡΠ΅ΠΌΡ ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡΠΎΡΠ½ΠΎΠ³ΠΎ ΠΈΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ.
ΠΡΠΈΠΌΠ΅Ρ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ Π½Π΅Π±Π»ΠΎΠΊΠΈΡΡΡΡΠ΅ΠΉ Π·Π°ΠΏΠΈΡΠΈ ΠΆΡΡΠ½Π°Π»Π° Π² ΡΠ°ΠΉΠ» (Π»ΡΡΡΠ΅ Π²ΡΠ΅Π³ΠΎ ΠΊΠ°ΠΊ JSON), ΠΎΠ΄Π½ΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎ Π²ΡΠ²ΠΎΠ΄ Π½Π° ΡΠΊΡΠ°Π½ ΠΈ ΠΎΡΠ³Π°Π½ΠΈΠ·Π°ΡΠΈΡ ΡΠ°ΡΠΎΠ²ΠΎΠ³ΠΎ ΠΆΡΡΠ½Π°Π»Π° Ρ Π΄ΠΎΠ·Π°ΠΏΠΈΡΡΡ (rolling):
use tracing::{instrument, warn};
// ΡΡΠ½Π΅ΠΌ std::io::Write ΠΏΡΠΈΠ·Π½Π°ΠΊ Π² ΡΠ΅ΠΆΠΈΠΌΠ΅ Π½Π΅Π±Π»ΠΎΠΊΠΈΡΠΎΠ²Π°Π½ΠΈΡ
use tracing_subscriber::fmt::writer::MakeWriterExt;
fn setup_tracing() {
// ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΡΠ°ΠΉΠ»Π° Ρ Π΄ΠΎΠ·Π°ΠΏΠΈΡΡΡ
let logfile = tracing_appender::rolling::hourly("/some/directory", "app-log");
// ΡΡΠΎΠ²Π΅Π½Ρ Π·Π°ΠΏΠΈΡΠΈ ΠΏΡΠΈ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ = INFO
let stdout = std::io::stdout.with_max_level(tracing::Level::INFO);
let subscriber = tracing_subscriber::fmt()
.with_max_level(tracing::Level::TRACE)
.json()
.compact()
.with_file(true)
.with_line_number(true)
.with_thread_ids(true)
.with_target(false)
.with_writer(stdout.and(logfile)) // ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΠΎ ΡΠΊΠ°Π·Π°ΡΡ Π·Π°ΠΏΠΈΡΡ ΡΡΡ
.finish(); // Π² ΡΠ°ΠΉΠ» ΠΈ Π² stdout ΠΊΠΎΠ½ΡΠΎΠ»Ρ
tracing::subscriber::set_global_default(subscriber).unwrap();
}
#[instrument]
fn sync_tracing() {
warn!("event 1");
sync_tracing_sub();
}
#[instrument]
fn sync_tracing_sub() {
warn!("event 2");
}
fn main() {
setup_tracing();
sync_tracing();
}