For human readable output, it is desirable for consumers as well as the framework to be able to:
- Output arbitrary types.
- Have them formatted by the output implementation.
- Not require the output implementation to know the specific type that is being output.
OutputWrite trait was written to handle different output formats – human-readable vs CI logging vs structured text – but not how to present the output.
Peace should provide two traits:
OutputWrite: Maps between the information and the output format, e.g. human readable vs parseable.
- CLI output: Writes progress as log messages (CI), or progress bar (interactive), or nothing (when piped).
- Web output: Write JSON.
- Native application: Update a UI component.
Presentable: Maps between the information and the presentation format, e.g. present this as an ID, short text, long text, a list, etcetera.
- CLI output: Colour text with ANSI colour codes.
- Web output: Create and style web elements.
- Native application: Create and style UI components.
OutputWrite trait has methods for:
- writing progress: setting up state, updating progress, and ending.
- writing states (current, desired, diff, etc.)
- writing error
These methods are specific to
States, and if we add methods per type, it doesn't allow any arbitrary type to be formatted and written.
To be usable with arbitrary information,
OutputWrite should have methods to output different kinds of information. These information kinds are based on the purpose of the information, not on how they should be grouped or presented.
Progress: Information about the execution of automation.
Outcome: Information that the automation is purposed to produce.
Notes: Meta information about the outcome or progress -- informatives, warnings.
These can be used to refine the automation.
For each information kind,
OutputWrite should be able to:
- Write one or many of that information kind
- Reason over the parameters of that information, and potentially pass it to a formatter.
For structured output, all information should be serializable.
Presentation / Formatting
For human readable output to be understandable, the
OutputWrite implementation should improve clarity by adding styling or reducing information overload. For this to work with arbitrary types, the
OutputWrite needs additional hints to determine how to format the information.
- An object may be presented as a list, and the type needs to define which fields that list is built from.
- When presenting a list of named items, the type needs to define both the name and the description, which allows the names to be styled differently to the descriptions.
- When presenting a large object, the density of information can be reduced through collapsible sections, and more detail displayed when the sections are expanded.
To achieve this, we can:
peace::fmt::Presentabletrait, analogous to
peace::fmt::Presentertrait, analogous to
Presenterhas methods to format:
- short text descriptions
- long text descriptions (e.g. always split at
- names, e.g. always bold
- lists of
- groups, e.g. always collapsible, or presenter may choose to not display if level of detail is too high
impl Presentable for MyType. This can be made easier with a derive macro.
OutputWriteto take in
&dyn Presentableinstead of concrete types, and the
OutputWriteimplementation can decide whether or not to delegate to
Presenterfor presentation information. e.g. a serializing output write may not need to.
Note: Structured output that is read by humans (e.g. prettified YAML or JSON) is not a
peace::fmt::Presentable concern, but an
OutputWrite parameter, as it is a standard format serialization parameter, not formatting hints that the output endpoint needs.
Instead of using
&strs for what is presented, we could add type safety to:
- Enforce certain constraints, e.g. short descriptions must be one line, less than 200 characters
- For human readable output, instead of
std::fmt::Display, types implement
peace::fmt::Presentabletrait where a
peace::fmt::Presenteris passed in.
The type safety can be opt-in, e.g. allow
&strs, but if using the type-safe wrappers, you get compilation errors when the constraints are not met.
Presentable trait is recursive like
Debug, then we need to make sure implementors understand that a "name" will always be styled as a name, unless one creates a wrapping type that does not delegate to the name's underlying
Presentable implementation (just like