Skip to content

Developer views

Four views added in 0.0.16 show the things a developer reads every day: a file's merge conflicts, one record of a data set, a crate's supply chain, and what changed between two schemas. Each is a renderable in a library crate and a flag of an existing rich command, so it reads the same in a program, a script and a terminal. This page says what each is for and links to its detailed section.

View Library Command Detail
Merge conflicts rich_ext::diff::ConflictView rich diff --conflicts FILE Merge conflicts
One record rich_ext::data::RecordView The record inspector
The supply chain rich_ext::deps::{duplicates, features, timings, audit, licenses} rich deps --duplicates, --features, --timings, --audit, --licenses Supply-chain reports
Schema changes rich_ext::schema::{SchemaDiff, SchemaTimeline} rich schema OLD NEW Schemas

None of them needs colour to be read: markers (< | > for conflict sides, + - ~ for changes) and words (breaking, FAIL, a severity) carry the meaning, and every style comes from a theme key a theme can override.

Merge conflicts

ConflictFile::parse reads the markers git merge leaves in a file, including diff3 bases and the longer markers of nested conflicts and conflict-marker-size, and ConflictView shows each conflict numbered, with ours, base and theirs side by side or stacked, syntax highlighted, and a few lines of context. Line numbers are the file's, so they lead back to the markers. Malformed markers are an error naming the line.

const MERGED: &str = "\
fn timeout() -> u64 {
<<<<<<< HEAD
    30
||||||| merged common ancestors
    10
=======
    env_or(\"TIMEOUT\", 10)
>>>>>>> feature/env
}
";

fn show_conflicts(console: &Console) {
    let file = ConflictFile::parse(MERGED).expect("well-formed markers");
    let conflict = &file.conflicts()[0];
    assert_eq!(file.text(&conflict.ours), "    30");
    assert_eq!(conflict.theirs.label.as_deref(), Some("feature/env"));
    // Side by side when every column fits, stacked otherwise.
    console.print(&ConflictView::new(file).path("src/config.rs"));
}

A diff3 conflict, ours, base and theirs side by side, with the file's line numbers

For a file on disk, ConflictView::parse(&std::fs::read_to_string(path)?)? parses and wraps it in one step, and .layout(ConflictLayout::Stacked) (or SideBySide) overrides the automatic choice.

git diff --name-only --diff-filter=U | xargs -n1 rich diff --conflicts

The record inspector

RecordView shows one record (a parsed JSON, YAML or TOML document, or any serde value) as a field | type | value table, with nested values as trees opened a few levels deep, long strings and containers cut, and expand(path) / collapse(path) to open or fold a branch by path. Redact before display with redact(…). It is the static half of rich_interact's data explorer: both implement OpenBranch.

fn show_record(console: &Console) {
    let record = parse(
        Format::Json,
        r#"{"id": 7, "name": "web", "password": "hunter2",
            "ports": [80, 443, 8080, 8443],
            "owner": {"team": "infra", "oncall": {"primary": "ana", "backup": "bo"}}}"#,
    )
    .unwrap();
    let view = RecordView::new(&record)
        .depth(1) // a nested value's own children show (the default)
        .expand("owner.oncall".parse().unwrap()) // and this branch, however deep
        .max_items(3) // then `… N more`
        .redact(&Redaction::secrets());
    console.print(&view);
}

A record as a field, type and value table: the password redacted, ports cut after three, and the owner.oncall branch expanded

The supply chain

Five reports beside rich deps' dependency tree read what Cargo already wrote, and never run a scanner or reach the network:

  • Duplicates: the crates in the graph at more than one version, the branches that pull each in, and a consolidation summary (detail).
  • Features: each crate's enabled features, what they turn on and who asked for them (detail).
  • Build times: the slowest units of a cargo build --timings report as bars and a table (detail).
  • Advisories: cargo audit --json output grouped by severity (detail).
  • Licences: crates grouped by licence, with copyleft, unknown and missing licences marked (detail).

The advisories report, from a cargo audit --json file:

fn audit(console: &Console) -> Result {
    // `cargo audit --json > audit.json`
    let report = AdvisoryReport::from_cargo_audit(&fixture("supply-chain/cargo-audit.json"))?;
    console.print(&report);
    Ok(())
}

Two vulnerabilities and two warnings, grouped by severity, each with its ID, crate, patched versions and title

Schema changes

SchemaDiff lists what changed between two schemas (JSON Schema, SQL DDL or Arrow, through one model), marking breaking the changes that may refuse data the old schema accepted; SchemaTimeline lays several versions side by side. The schema guide covers both, with the tree and ER views.

fn diff(console: &Console) -> Result {
    let v1 = sql::parse(V1)?.schema;
    let v2 = sql::parse(V2)?.schema;
    console.print(&SchemaDiff::models(&v1, &v2).names("v1", "v2"));
    Ok(())
}

Six changes between two versions of a shop's DDL, three marked breaking

From Python

ConflictView and RecordView are in rs_rich.data (and in rs_rich.ext.diff and rs_rich.ext.data), with the schema views. The Cargo reports are the rich deps command, which python -m rs_rich runs.