36
loading...
This website collects cookies to deliver better user experience
exa
command-line tool from Github and see what crates it used. Better to look at a released tool that is out in the wild?ls
. It allows me to produce listings like:$ cargo tree
ansi_term
datetime
git2
glob
lazy_static
libc
locale
log
natord
num_cpus
number_prefix
scoped_threadpool
term_grid
term_size
unicode-width
users
zoneinfo_compiled
bitflags
byteorder
matches
pad
tinyvec
url
use ansi_term::Colour::Yellow;
println!("My name is {}", Yellow.bold().paint("Matt"));
&str
and String
. The one that I use frequently is colored
. For example, to recreate the last snippet using colored
, it is:use colored::Colorize;
println!("My name is {}", "Matt".yellow().bold());
date_time
that does similar stuff. The datetime
library here provides structures for representing a calendar date and time of day in both timezone and local form. It also provides formatting functions that convert a date or time into a string and back again, but unfortunately no documentation on how to use it. The format
function takes a time value but also takes a locale of type Time
but no information on how to generate that. I couldn't figure it myself.chrono
. It is fully featured, efficient and better documented. You can do UTC, local and fixed offset times (times in a certain timezone). It can format to and parse from strings containing times and dates. Times also have nanosecond accuracy. Times and dates are complicated things and chrono
is a useful weapon in your coding arsenal.typedef enum Flags {
A = 0x01,
B = 0x02,
C = 0x04,
ABC = A | B | C,
};
int ab = A | B;
bitflags
you can:use bitflags::bitflags;
bitflags! {
struct Flags: u8 {
const A = 0b001;
const B = 0b010;
const C = 0b100;
const ABC = Self::A.bits | Self::B.bits | Self::C.bits;
}
}
let ab = Flags::A | Flags::C;
enum class
. However, Rust's bitflags
crate does support set difference using the -
operator. This would go very wrong in C and C++ as -
would be treated as a normal integer subtract. For example:let ac = Flags::ABC - Flags::B;
let we_dont_want_b = ac - Flags::B;
to_le_bytes
et al. family of functions on the integer primitive types so a lot of this crate is redundant now. Where this crate is useful is with implementing the Read
and Write
interfaces.u32
it takes 4 bytes of storage. There are 2 conventional ways of storing this value. You could put the high bytes first (big-endian) or the low bytes first (little-endian) into memory. So for example, the number 42
could be stored as the bytes 42, 0, 0, 0
or the bytes 0, 0, 0, 42
. Most modern CPUs, by default, support the former, which is little-endian. However, data that goes over the network is usually big-endian. So these routines are critical for putting the data in the correct form. There is also a third convention called native-endian
that is either little or big depending on the CPU's preferred form.libgit2
library. exa
uses this to implement its .gitignore
support. This is a large crate and way beyond the scope of this article.exa
is to iterate over all the files in a directory. glob
does this with the bonus that you can use the wildcards *
and **
. It provides a single function glob
that takes a file pattern and gives back an iterator returning paths. For example:// Read in all the markdown articles under all folders in my blogs folder.
use glob::glob;
for entry in glob("~/blogs/**/*.md").unwrap() {
match entry {
// path is a PathBuf
Ok(path) => println!("{}", path),
// e is a GlobError
Err(e) => eprintln!("{}", e),
}
}
filter
, map
etc. But there is no need to sort since paths are yielded in alphabetical order.fn main() {
println!("Number = {}", MY_STATIC);
}
static MY_STATIC: u32 = foo();
fn foo() -> u32 {
42
}
error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
--> src/main.rs:5:25
|
5 | static MY_STATIC: u32 = foo();
| ^^^^^
lazy_static
it becomes:use lazy_static::lazy_static;
fn main() {
println!("Number = {}", *MY_STATIC);
}
lazy_static! {
static ref MY_STATIC: u32 = foo();
}
fn foo() -> u32 {
42
}
lazy_static!
macro to wrap the static declarations. Secondly, there's an added ref
keyword. The statics returned here are references to your type. Using them invokes the Deref
trait. This means that thirdly, I had to dereference it so that the Display
trait was detectable for u32
. In Rust, Deref
is not invoked when looking for traits so I had to do it manually.libc
provides more support to interoperate with C code. It adds type definitions (like c_int
), constants and function headers for standard C functions (e.g. malloc
).0.3
. This provides information on how to format numbers and time.use locale::*;
fn main() {
let mut l = user_locale_factory();
let numeric_locale = l.get_numeric().unwrap();
println!(
"Numbers: decimal sep: {} thousands sep: {}",
numeric_locale.decimal_sep, numeric_locale.thousands_sep
);
let time = l.get_time().unwrap();
println!("Time:");
println!(
" January: Long: {}, Short: {}",
time.long_month_name(0),
time.short_month_name(0)
);
println!(
" Monday: Long: {}, Short: {}",
time.long_day_name(1),
time.short_day_name(1)
);
}
Numbers: decimal sep: . thousands sep: ,
Time:
January: Long: January, Short: Jan
Monday: Long: Mon, Short: Mon
time.long_day_name(1)
should return Monday
and not Mon
. Whether this is an operating system issue or a problem with locale
, I am not sure.env_logger
, simple_logger
and few other crates. log
is not used directly by exa
itself, but rather some of its dependencies.error
and warn!
to pass formatted messages to a logger. There are multiple levels of logging and they range from trace!
to error!
in order of rising priority:fatal!
as most logging systems contain these levels.set_max_level
function. By default, it is set to Off
and no messages are sent to loggers. Levels can also be set at compile-time using various features. All of this is described in the documentation.Log
trait and users install them by calling the set_logger
function.// Macro version
let b = macros!(my_expr, Foo::A(_));
// is the same as:
let b = match my_expr {
Foo::A(_) => true,
_ => false,
}
// or even:
let b = if let Foo::A(_) = my_expr { true } else { false };
["foo169", "foo42", "foo2"]
, you would get the sequence ["foo169", "foo2", "foo42"]
. This might not be the order you would prefer. What you might want is sometimes referred to as normal ordering. You might prefer the order ["foo2", "foo42", "foo169"]
where the numbers in the strings increase in value and not by ASCII ordering.natord
provides. Natural ordering can work well for filenames with numbering inside them and IP addresses to name just a couple. For example, natural ordering will handle strings where the numbers are in the middle (e.g. "myfile42.txt") and not just at the end.get()
function to obtain the number of logical cores on the running system, and get_physical()
function to obtain the number of physical cores. Very useful if you want to set up a worker thread pool.K
will not be used. Rather Ki
will be used.K
, but call upper()
on it and you will get KILO
, call lower()
for kilo
and caps()
for Kilo
.exa
is required to do.format!
function can do a lot of padding functionality but this crate can do more.let s = "to the right!".pad_to_width_with_alignment(20, Alignment::Right);
'static
lifetime or are entirely owned inside the thread.fn main() {
// We create a pool of 4 threads to be utilised later.
let mut pool = Pool::new(4);
// Some data we can do work on and reference to.
let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7];
// Use the pool of threads and give them access to the scope of `main`.
// `vec` is guaranteed to have a lifetime longer than the work we
// will do on the threads.
pool.scoped(|scope| {
// Now we grab a reference to each element in the vector.
// Remember `vec` is still around during `pool.scoped()`.
for e in &mut vec {
// Do some work on the threads - we move the reference
// in as its mutably borrowed. We still cannot mutably
// borrow a reference in the for loop and the thread.
scope.execute(move || {
// Mutate the elements. This is allowed as the lifetime
// of the element is guaranteed to be longer than the
// work we're doing on the thread.
*e += 1;
});
}
});
}
pool.scoped
must block until all work is done to allow this to happen.ls
output, exa
must show filenames in a grid formation. Given a width, this crate provides a function fit_into_width
that can help to produce a grid of strings. By working out the longest string in a collection, it can calculate how many of those strings can fit in horizontal space, like say, the line on a terminal.let (width, height) = match term_size::dimensions() {
Some((width, height)) => (width, height),
None => (80, 25),
}
dimensions()
is an Option<(usize, usize)>
.ArrayVec
and TinyVec
.ArrayVec
is a safe array-backed drop-in replacement for Vec
. This means that a fixed array is allocated to act as storage for ArrayVec
. In any other way, ArrayVec
acts like a Vec
but it cannot reallocate that storage. If the array becomes too big a panic will occur.ArrayVec
even allows access to the unused space in the backing array that is currently being used for elements.ArrayVec
uses a fixed array, a heap allocation does not occur.TinyVec
is a drop-in replacement for Vec
too. However, for small arrays, it starts life as an ArrayVec
using a fixed array as the backing store. As soon as it becomes too big, it will automatically revert to use a normal Vec
, hence using the heap to store the array.alloc
feature to be activated.ArrayVec
or a Vec
:enum TinyVec<A: Array> {
Inline(ArrayVec<A>),
Heap(Vec<A::Item>),
}
let my_vec = tiny_vec!([u8; 16] => 1, 2, 3);
Vec<u8>
.exa
needs to deal with. Fortunately, there is a crate that can provide that information.unicode-width
does remind us that the character width may not match the rendered width. So be careful but I don't think there is much you can do in these situations.http
)docs.rs
)http://docs.rs:8080
)foo/bar
in the URL http://docs.rs/foo/bar
)?
in myurl.com/foo?var=value
)Url::parse
function that constructs an object describing the various parts of an URL. Various methods can then be called to provide string slices into the URL such as scheme()
or host()
.serde
support too so if you know what that is, you will understand what this means. Maybe I will write about serde
in a future article.users
crate provides this functionality by wrapping the standard C library functions that can obtain user and group information.exa
work on Windows so I am assuming.zoneinfo
files. This information allows you to obtain information on leap seconds and daylight savings time for your current time zone./usr/share/zoneinfo
folder. Each timezone has a binary file and this crate can parse this file and extract the information it holds.exa
would be using this crate. It's a very complex topic and something I would love to dig in deeper with and hopefully not go insane at the same time.tz database
and you can find more information about it, if you so desire, at Wikipedia.