Libraries

Articles in Section

Library Unit-tests

ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹:

pub fn greet_user(name: &str) -> String {
    format!("Hello {name}")
}

pub fn login(username: &str, password: &str) -> bool {
    username == "admin" && password == "P@ssw0rd"
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_greet_user() {
        assert_eq!("Hello Alex", greet_user("Alex"));
    }

    #[test]
    fn test_login() {
        assert!(login("admin", "P@ssw0rd"));
    }
}

Запуск тСстов - ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ cargo test

Subsections of Libraries

Anyhow Error Handler

Π‘ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° Ρ€Π°Π±ΠΎΡ‚Ρ‹ с ошибками - https://docs.rs/anyhow/latest/anyhow/

  • Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ Π΅Π΄ΠΈΠ½Ρ‹ΠΉ Ρ‚ΠΈΠΏ Error для всСх ошибок;
  • ДобавляСт контСкст ΠΊ ошибкам;
  • ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° backtrace для debug;

Установка

cargo add anyhow

ИспользованиС

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) Ρƒ структур Π΄Π°Π½Π½Ρ‹Ρ….

cargo add clap -F derive

Как ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ

Π”ΠΎΠ±Π°Π²ΠΊΠ° 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()?;

itertools

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

Π’Π½Π΅ΡˆΠ½ΠΈΠ΅ ссылки:

Установка

cargo add 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/

Установка

cargo add 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();
}