Rust 开发命令行 ls 命令
效果如下:
新建项目
cargo new bestls && cd bestls
添加依赖
修改 Cargo.toml,添加依赖 clap 和 owo-colors
[package]
name = "bestls"
version = "0.1.0"
edition = "2024"
[dependencies]
clap = { version = "4.5.39", features = ["derive"] }
owo-colors = "4.2.1"
strum = {version = "0.27", features = ["derive"]}
strum_macros = "0.27"
tabled = "0.20.0"
chrono = "0.4.41"
serde = {version = "1.0", features = ["derive"]}
serde_json = "1.0"
clap 命令行使用示例
修改 src/main.rs
use std::fs;
use std::path::PathBuf;
use owo_colors::OwoColorize;
use clap::Parser;
#[derive(Debug, Parser)]
#[command(version, about, long_about = "Best Ls command ever")]
struct Cli {
path: Option<PathBuf>,
}
fn main() {
let cli = Cli::parse();
let path = cli.path.unwrap_or(PathBuf::from("."));
if let Ok(does_exist) = fs::exists(&path) {
if does_exist {
} else {
println!("{}", "Path does not exist".red())
}
}
println!("{}", path.display());
}
构建
cargo build
测试
target/debug/bestls -h
target/debug/bestls -V
target/debug/bestls ~/Documents/1
完整代码
修改 src/main.rs
use std::fs;
use std::path::{Path, PathBuf};
use chrono::{DateTime, Utc};
use owo_colors::OwoColorize;
use clap::Parser;
use serde::Serialize;
use strum_macros::Display;
use tabled::{Table, Tabled};
use tabled::settings::object::{Columns, Rows};
use tabled::settings::{Color, Style};
#[derive(Debug, Parser)]
#[command(version, about, long_about = "Best Ls command ever")]
struct Cli {
path: Option<PathBuf>,
#[arg(short, long)]
json: bool,
}
#[derive(Debug, Display, Serialize)]
enum EntryType {
File,
Dir,
}
#[derive(Debug, Tabled, Serialize)]
struct FileEntry {
#[tabled(rename = "Name")]
name:String,
#[tabled(rename = "Type")]
e_type: EntryType,
#[tabled(rename = "Size B")]
len_bytes: u64,
modified: String
}
fn main() {
let cli = Cli::parse();
let path = cli.path.unwrap_or(PathBuf::from("."));
if let Ok(does_exist) = fs::exists(&path) {
if does_exist {
if cli.json {
let get_files = get_files(&path);
println!("{}", serde_json::to_string(&get_files).unwrap_or("cannot parse json".to_string()));
} else {
print_table(path);
}
} else {
println!("{}", "Path does not exist".red())
}
} else {
println!("{}", "error reading directory".red())
}
}
fn get_files(path: &Path) -> Vec<FileEntry > {
let mut data = Vec::default();
if let Ok(read_dir) = fs::read_dir(path) {
for entry in read_dir {
if let Ok(file) = entry {
map_data(file, &mut data);
}
}
}
data
}
fn map_data(file: fs::DirEntry, data: &mut Vec<FileEntry>) {
if let Ok(meta) = fs::metadata(&file.path()) {
data.push(
FileEntry {
name: file.file_name().into_string().unwrap_or("unknown name".into()),
e_type: if meta.is_dir() {EntryType::Dir} else {EntryType::File},
len_bytes:meta.len(),
modified: if let Ok(modi) = meta.modified() {
let date: DateTime<Utc> = modi.into();
format!("{}", date.format("%a %b %e %Y"))
} else {
String::default()
}
}
);
}
}
fn print_table(path: PathBuf) {
let get_files = get_files(&path);
let mut table = Table::new(get_files);
table.with(Style::rounded());
table.modify(Columns::first(), Color::FG_BRIGHT_CYAN);
table.modify(Columns::one(2), Color::FG_BRIGHT_MAGENTA);
table.modify(Columns::one(3), Color::FG_BRIGHT_YELLOW);
table.modify(Rows::first(), Color::FG_BRIGHT_GREEN);
println!("{}", table)
}
编译
cargo build
测试
target/debug/bestls ./target
target/debug/bestls ./target -j