Struct conciliator::print::List

source ·
pub struct List<H, I, F, P> { /* private fields */ }
Expand description

List of things to print, with a short description

§Introduction

For example:

let con = conciliator::init();
use conciliator::{Conciliator, List};
con.print(List::new("numbers", 0..3));

Prints:

[ > ] 3 numbers:
        → 0
        → 1
        → 2

The basic List::new:

  • indents the items and adds a little arrow:
  • introduces the list with a description and its length
  • does not print anything if the iterator is empty
  • works for any ExactSizeIterator whose items implement either Inline or Display

While none of these small features are exceptionally tedious to write manually, to do so frequently would be very inconvenient, and so, this struct was created.

§Implementation

It’s not necessary to understand these implementation details, this section may be skipped.

The List is rather flexible, which unfortunately means it has several generic parameters:

  • the Prefixer controls how/if the items are indented,
  • the Formatter formats and prints the items
  • the Header can print an introduction at the beginning

The “default” Formatter (i.e. what the constructors for this type return) is a PushableFmt<M>. The M here is generic without constraints: just like with Pushable, it is up to type inference at the call site to pick the correct one. So, for a List of Inline or Display items, the correct marker is inferred when it is printed because printing is only implemented when formatting is. When the List is given a different Formatter using with_formatter or with_wrap, the generic M is inferred to be (). This is because those methods are only implemented for PushableFmt<()> (which is useless) - if they were implemented for any M, using them would somewhat counter-intuitively require type annotations for a type that’s being replaced anyway.

§Method overview

Constructors:

Builders:

Printing:

§Examples

Minimal example:

use conciliator::{Conciliator, List};
let con = conciliator::init();
con.print(List::new("numbers", 0..3));

Basic example:

use conciliator::{Conciliator, List, Wrap};
let con = conciliator::init();
let uuids = [
    "37312de1-3f29-4db2-a437-b03de26ce06d",
    "239229f8-ff65-4986-8ca7-44ce65a6d66b",
    "61951efa-e5f6-422e-862c-a793af716866"
];
let list = List::new("UUIDs", uuids.iter()).with_wrap(Wrap::Alpha);
con.print(list);

Alternatively, the last two lines can be rewritten as:

List::new("UUIDs", uuids.iter())
    .with_wrap(Wrap::Alpha)
    .print_to(&con);

Either way, both print:

[ > ] 3 UUIDs:
        → 37312de1-3f29-4db2-a437-b03de26ce06d
        → 239229f8-ff65-4986-8ca7-44ce65a6d66b
        → 61951efa-e5f6-422e-862c-a793af716866

Finally, a slightly more complicated example that prints the environment variables. It utilizes a custom wrapper type and prints an error if the iterator yields no items:

use conciliator::{Conciliator, Buffer, Paint, Inline, List};
let con = conciliator::init();

struct EnvVar((String, String));
impl Inline for EnvVar {
    fn inline(&self, buffer: &mut Buffer) {
        buffer
            .push_beta_bold(&self.0.0)
            .push(": ")
            .push(&self.0.1);
    }
}

let printed = List::headless(std::env::vars())
    .with_header("Environment variables")
    .with_wrap(EnvVar)
    .print_and_count(&con);
if printed == 0 {
    con.error("No environment variables set!");
}

The result looks like this (though this is truncated and without colors):

[ > ] Environment variables:
        → CARGO_CRATE_NAME: conciliator
        → CARGO_PKG_LICENSE: GPL-3.0-or-later
        → CARGO_PKG_NAME: conciliator
        → CARGO_PKG_REPOSITORY: https://git.sr.ht/~xaos/conciliator
        → CARGO_PKG_VERSION: 0.3.3

Check out the example binary to see it in action!

Implementations§

source§

impl<I, T, MT, H, MH> List<PushableHdr<MH, H>, I, PushableFmt<MT>, ArrowPrefix>
where I: ExactSizeIterator<Item = T>,

source

pub fn new(header: H, iter: I) -> Self

A List introduced by a header, showing the amount of items in it

Requires iter to be an ExactSizeIterator.

The header may be any implementor of Pushable - which is any type implementing Inline or Display (for types that do both it gets a little finicky).

So, for example:

con.print(List::new("numbers", 0..3));

Prints:

[ > ] 3 numbers:
        → 0
        → 1
        → 2

Note that the header here is only "numbers"; the tag, amount 3, and the : are added automatically.

The returned List is Printable as-is if the items are Pushable. Otherwise, one of List::with_formatter or List::with_wrap has to be used to specify how the items should be printed.

struct Number(usize); // doesn't implement Display or Inline
List::new("numbers", (0..3).map(Number))
    .with_wrap(|n| Wrap::Alpha(n.0))
    .print_to(&con);
source§

impl<I, MT, H, MH> List<PushableHdr<MH, H>, I, PushableFmt<MT>, ArrowPrefix>

source

pub fn uncounted(header: H, iter: I) -> Self

A List introduced by a header

Just like List::new() but without showing the amount of items. Consequently does not require an ExactSizeIterator.

The header may be any implementor of Pushable - which is any type implementing Inline or Display (for types that do both it gets a little finicky).

So, for example:

con.print(List::uncounted("Some numbers", (0..).take(3)));

Produces something like:

[ > ] Some numbers:
        → 0
        → 1
        → 2

Note that the header here is only "Some numbers". The tag and the : are added automatically.

source§

impl<I, M> List<NoHeader, I, PushableFmt<M>, ArrowPrefix>

source

pub fn headless(iter: I) -> Self

A List without introduction

By itself, the list returned by this prints only the items without any header:

con.print(List::headless(0..3));
        → 0
        → 1
        → 2

However, a header can be added using List::with_header (without the count of items) or List::with_count (with).

con.print(List::headless(0..3).with_count("numbers"));
[ > ] 3 numbers:
        → 0
        → 1
        → 2
source§

impl<I, M> List<NoHeader, I, PushableFmt<M>, Indexer>

source

pub fn indexed(iter: I) -> Self

A List printing each item with its index, starting at 1 (without introduction)

By itself, the list returned by this does not print a header:

con.print(List::indexed(0..3));
        [1] - 0
        [2] - 1
        [3] - 2

A header can be added using List::with_header (without the count of items) or List::with_count (with).

con.print(List::indexed(0..3).with_count("numbers"));
[ > ] 3 numbers:
        [1] - 0
        [2] - 1
        [3] - 2
source§

impl<H, I, P> List<H, I, PushableFmt<()>, P>

source

pub fn with_formatter<F>(self, formatter: F) -> List<H, I, F, P>

Use a Formatter to format items

Here’s a simple example using a Formatter to write OsStrings into the Buffer losslessly.

use std::ffi::OsString;
use std::os::unix::ffi::OsStrExt;
use std::io::Write;
use conciliator::{List, Buffer};
let con = conciliator::init();

fn format_os_string(buf: &mut Buffer, os_string: OsString) {
    buf.write_all(&os_string.as_bytes()).unwrap();
}

List::new("arguments", std::env::args_os())
    .with_formatter(format_os_string)
    .print_to(&con);

This prints the arguments to the process unchanged, even if they contain invalid UTF-8 for some reason.

[ > ] 1 arguments:
        → /tmp/rustdoctestyyoYAo/rust_out

Unfortunately, it also shows some of the limitations of the Formatter trait: for one, fallible formatting is not supported, which is why the std::io::Result from write_all is simply unwrapped (not that there’s much error handling to be done when writing to a buffer fails). And secondly, the arguments to the formatting function need to match the iterator exactly, i.e. format_os_string has to take an OsString by value even though intuitively, taking the OsString by reference or even an &OsStr would be sufficient here.

Another slight inconvenience is that, while a closure can be used as a Formatter, doing so will require type-annotating its arguments.

List::new("arguments", std::env::args_os())
    .with_formatter(|buf: &mut Buffer, os_string: OsString| {
        buf.write_all(&os_string.as_bytes()).unwrap();
    })
    .print_to(&con);

This may be improved in the future by adding a method that is less general.

source§

impl<H, I: Iterator<Item = T>, T, P> List<H, I, PushableFmt<()>, P>

source

pub fn with_wrap<F, W>(self, wrap_fn: F) -> List<H, I, WrapFmt<F>, P>
where F: FnMut(T) -> W, W: Inline,

Use a function or closure to wrap items for printing

Here is the minimal example adjusted to print the numbers with the Alpha color:

List::new("numbers", 0..3)
    .with_wrap(Wrap::Alpha)
    .print_to(&con);

Unfortunately the terminal formatting doesn’t render here, but the results are unsurprising (only the numbers themselves are colored).

The wrapper function gets applied to each item, in order. See WrapFmt for more.

source§

impl<I, F, P> List<NoHeader, I, F, P>

source

pub fn with_header<M, T>(self, header: T) -> List<PushableHdr<M, T>, I, F, P>

Use something Pushable to introduce the list

Doesn’t add the count of items automatically.

source§

impl<I: ExactSizeIterator<Item = T>, T, F, P> List<NoHeader, I, F, P>

source

pub fn with_count<M, H>(self, header: H) -> List<PushableHdr<M, H>, I, F, P>

Use something Pushable to introduce the list, showing the count

The count of items gets added automatically, before header.

source§

impl<H, I, T, P, F> List<H, I, F, P>
where H: Print, I: Iterator<Item = T>, P: Prefixer, F: Formatter<T>,

source

pub fn print_and_count<C>(self, con: &C) -> usize
where C: Conciliator + ?Sized,

Print this List with a Conciliator and return the count of items

The List doesn’t print anything if there are no items, but the user might need to be informed if the iterator was expected to not be empty. Since Print::print does not have a return value and because the iterator needs to be consumed fully to know how many items it contained, this method must be used instead of Print::print to also return the count.

let search = (0..10).filter(|i| i % 3 == 0);
let printed_something = List::uncounted("Numbers found", search)
    .with_wrap(Wrap::Beta)
    .print_and_count(&con)
    .gt(&0);
if !printed_something {con.error("No numbers found!");}
source

pub fn print_to<C: Conciliator + ?Sized>(self, con: &C)

Print this List with a Conciliator

This is exactly the same as using the Print::print function, provided only for the convenience of not needing to have Print in scope.

When using the with_* methods to build the List, this composes nicer than using the .print(…) method on the Conciliator. For example, this:

List::headless(0..3)
    .with_count("numbers")
    .with_wrap(Wrap::Beta)
    .print_to(&con);

is nicer than this:

con.print(List::headless(0..3).with_count("numbers").with_wrap(Wrap::Beta));

Trait Implementations§

source§

impl<H, I, T, P, F> Print for List<H, I, F, P>
where H: Print, I: Iterator<Item = T>, P: Prefixer, F: Formatter<T>,

source§

fn print<C: Conciliator + ?Sized>(self, con: &C)

Print self using the Conciliator, consuming self

Auto Trait Implementations§

§

impl<H, I, F, P> RefUnwindSafe for List<H, I, F, P>

§

impl<H, I, F, P> Send for List<H, I, F, P>
where F: Send, H: Send, I: Send, P: Send,

§

impl<H, I, F, P> Sync for List<H, I, F, P>
where F: Sync, H: Sync, I: Sync, P: Sync,

§

impl<H, I, F, P> Unpin for List<H, I, F, P>
where F: Unpin, H: Unpin, I: Unpin, P: Unpin,

§

impl<H, I, F, P> UnwindSafe for List<H, I, F, P>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.