Crate pfmt[−][src]
Overview
This library provides a flexible and powerful method to format data. At the
heart of the library lie two traits: Fmt
, for things that are formattable,
and FormatTable
, that maps placeholders in format strings to actual Fmt
s
and supply those with information they need to produce output. Unlike with
format!
from the standard library, there is no restriction that format
strings need to be static; in fact the whole point of the library is to
allow moving as much control over formatting process into the format strings
themselves (and ideally those - in user-editable config files).
There are several impl
s of FormatTable
, most notable for HashMap
s
(with either str
or String
keys, and Borrow<Fmt>
values, which means a
bit of type annotations required to use those) and Vec
s (with
Borrow<Fmt>
elements). The method on FormatTable
to format a string is
format(&self, format_string: &str) -> Result<String, FormattingError>
Each format string consists of one or several literals and placeholders,
optionally separated by colons (":
"). If you need a colon in your literal
or some part of a placeholder, you need to escape it: "\:"
. In its
simplest form, a placeholder looks like this: "{foobar}"
(brackets can be
escaped too, if you need them in a literal or somewhere else). This will
request the format table that does the formatting to lookup a Fmt
named
"foobar" and insert it contents there, or fail if it cannot find it (or
produce it - see 'More fun' section). Of course, this alone is not much use.
Most Fmt
s support flags that can change the output of the formatting
procedure, for instance floats can request to be printed in exponential
notation. Flags are just single characters and are separated from the
identifier with a colon: "{foobar:flags}"
. A trailing colon is allowed.
Some Fmt
s also support options, which are specified after the flags (and
if you want to use options, you need a flags section, even if it's empty)
and are separated by colons: "{foobar::option1=value1:option2=value2}"
.
There aren't too many options at the moment. There is also a possibility of
giving arguments to a placeholder, but there's no implementation (yet) of a
FormatTable
that takes advantage of it.
See each implementation's entry to learn all the options and flags it supports.
So let's see some examples how this all is tied together.
Examples
Let's start with something boring:
use std::collections::HashMap; use pfmt::{Fmt, FormatTable}; let i = 2; let j = 5; let mut table: HashMap<&str, &Fmt> = HashMap::new(); table.insert("i", &i); table.insert("j", &j); let s = table.format("i = {i}, j = {j}").unwrap(); assert!(s == "i = 2, j = 5");
I can do that with format!
too. This is a bit more fun, and shows both
options and flags:
use std::collections::HashMap; use pfmt::{Fmt, FormatTable}; let s = "a_really_long_string"; let i = 10; let j = 12; let mut table: HashMap<&str, &Fmt> = HashMap::new(); table.insert("s", &s); table.insert("i", &i); table.insert("j", &j); // (note escaped colons) let s = table.format("hex\\: {i:px}, octal\\: {j:o}, fixed width\\: {s::truncate=r5}").unwrap(); assert!(s == "hex: 0xa, octal: 14, fixed width: a_rea");
Can't decide if you want your booleans as "true" and "false", or "yes" and "no"? Easy:
use std::collections::HashMap; use pfmt::{Fmt, FormatTable}; let a = true; let b = false; let mut table: HashMap<&str, &Fmt> = HashMap::new(); table.insert("a", &a); table.insert("b", &b); let s = table.format("{a}, {b:y}, {b:Y}").unwrap(); assert!(s == "true, no, N");
And here are Vec
s as format tables:
use pfmt::{Fmt, FormatTable}; let i = 1; let j = 2; let table: Vec<&Fmt> = vec![&i, &j]; let s = table.format("{0}, {1}, {0}").unwrap(); assert!(s == "1, 2, 1");
All of the above examples used references as the element type of the format
tables, but FormatTable
is implemented (for hashmaps and vectors) for
anything that is Borrow<Fmt>
, which means boxes, and reference counters
and more. Tables can fully own the data:
use std::collections::HashMap; use pfmt::{Fmt, FormatTable}; let mut table: HashMap<String, Box<Fmt>> = HashMap::new(); table.insert("a".to_string(), Box::new(2) as Box<Fmt>); table.insert("b".to_string(), Box::new("foobar".to_string()) as Box<Fmt>); let s = table.format("{a}, {b}").unwrap(); assert!(s == "2, foobar");
This is a bit on the verbose side, though.
Errors
format
method on FormatTables
returns a Result<String, FormattingError>
. There are three primary types of these: parsing errors
which occur when the format string is not well-formed, errors arising from
usage of unknown options and flags or options with invalid values, and
finally errors due to requesting Fmt
s that are missing in the table.
With hard-coded format strings and rigid format tables, most of these can be
safely ignored, so unwrap()
away.
Common options
Most pre-made implementation of Fmt
honor several common options. Here's
a list of them, with detailed info available further in this section:
truncate
width
truncate
: {'l', 'r'} + non-negative integer
Controls truncation of the field. If begins with l
, left part of the
field that doesn't fit is truncated, if begins with r
- the right part is
removed instead. Note that "l0"
is not actually forbidden, just very
useless.
It is an InvalidOptionValue
to pass anything not fitting into the template
in the header as the value of this option.
width
: {'l', 'c', 'r'} + non-negative integer
Controls the width of the field. Has no effect if the field is already wider
than the value supplied. If starts with "l
", the field will be
left-justified. If starts with "c
", the field will be centered. If starts
with "r
", the field will be right-justified.
It is an InvalidOptionValue
to pass anything not fitting into the template
in the header as the value for this option.
Common numeric options
Most numeric Fmts honor these. For the detailed description skip to the end of this section.
prec
round
prec
: integer
Controls precision of the displayed number, with bigger values meaning more
significant digits will be displayed. If negative, the number will be
rounded, the rounding direction is controlled by the round
option.
Positive values are accepted by integer Fmts, but have no effect.
It is an InvalidOptionValue
to pass a string that doesn't parse as a
signed integer as a value to this option.
round
: {"up", "down", "nearest"}
Controls the direction of rounding by the round
option, and has no effect
without it. Defaults to nearest
.
It is an InvalidOptionValue
to pass a string different from the mentioned
three to this option.
More fun
Format tables are not required to actually hold the Fmt
s. They can
produce those on the fly, if you make them to. You only need to implement
produce_fmt
method:
use pfmt::{Fmt, FormatTable}; struct Producer { } impl FormatTable for Producer { fn get_fmt(&self, name: &str) -> Option<&Fmt> { None } fn produce_fmt(&self, name: &str) -> Option<Box<Fmt>> { if let Ok(i) = name.parse::<i32>() { Some(Box::new(i)) } else { None } } } let table = Producer { }; let s = table.format("{1}, {12}").unwrap(); assert!(s == "1, 12");
The above example is not particularly useful, but shows the point.
There's also an implementation of FormatTable
for tuples (up to 6-tuples)
that contain format tables. When encountering a placeholder, it first
searches for the relevant Fmt
in the first table, then in the second and
so on. This allows to easily override some Fmt
s or provide defaults
without changing the tables themselves.
use std::collections::HashMap; use pfmt::{Fmt, FormatTable}; let i1 = 10; let i2 = 100; let j = 2; let mut table1: HashMap<&str, &Fmt> = HashMap::new(); table1.insert("i", &i1); table1.insert("j", &j); let mut table2: HashMap<&str, &Fmt> = HashMap::new(); table2.insert("i", &i2); let s = (table2, table1).format("{i}, {j}").unwrap(); assert!(s == "100, 2");
Modules
util |
Enums
FormattingError |
Any error that can happen during formatting. |
SingleFmtError |
Errors that happen in individual formattables. |
Traits
Fmt |
This trait drives the formatting of a single placeholder. Placeholder's
arguments, flags and options are passed to the |
FormatTable |
Objects implementing this trait drive the formatting process by supplying
information to the contained or produced |