/pretty

Pretty printer for arbitrary data structures in Zig.

Primary LanguageZigMIT LicenseMIT

pretty 🪶

a simple pretty printer for arbitrary data structures in Zig⚡️

designed to inspect deeply-nested and recursive tree structures.

current version: v0.10.3 (tested on 0.13.0)

demo

simplified version of src/demo.zig:

// main.zig
const std = @import("std");
const pretty = @import("pretty.zig");
const alloc = std.heap.page_allocator;

pub fn main() !void {
    const array = [3]u8{ 4, 5, 6 };

    // method 1: print with default options
    try pretty.print(alloc, array, .{});

    // method 2: customize your print
    try pretty.print(alloc, array, .{
        .array_show_item_idx = false,
        .inline_mode = true,
    });

    // method 3: don't print, get a string!
    var out = try pretty.dump(alloc, array, .{});
    defer alloc.free(out);
    std.debug.print("{s}..\n", .{out[0..5]});
}

output:

$ zig run main.zig
[3]u8
  [0]: 4
  [1]: 5
  [2]: 6

[3]u8{ 4, 5, 6 }

[3]u8..

installation

there are three ways to use pretty:

  • git clone https://github.com/timfayz/pretty, cd pretty and zig build run, or
  • download latest version of pretty.zig and @import it directly inside your project, or
  • use zig build system and build.zig.zon (highly undocumented):
$ cd your_project
$ ls
src/
build.zig     # should be present (otherwise use `zig init` to generate it)
build.zig.zon # optional (otherwise will be generated by the next command)
$ zig fetch --save https://github.com/timfayz/pretty/archive/v0.10.3.tar.gz
$ cat build.zig.zon
..
  .pretty = .{
      .url = "https://github.com/timfayz/pretty/archive/v0.10.3.tar.gz",
      .hash = "..long_hash..",
  },
..

edit build.zig:

// find this:
const exe = b.addExecutable(.{
    .name = "your_app",
    .root_source_file = .{ .path = "src/main.zig" },
    .target = target,
    .optimize = optimize,
});

// add the following:
const pretty = b.dependency("pretty", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("pretty", pretty.module("pretty"));
..

done, build or run your project:

$ zig build # or zig build run

api

pretty offers four main functions:

  1. pretty.print – print formatted string to stdout.
  2. pretty.printInline – print formatted string with .inline_mode switched on.
  3. pretty.dump – generate formatted string and return it as []u8 slice.
  4. pretty.dumpList – generate formatted string and return it as std.ArrayList interface.

options

pretty output can be customized with the following options (default values in the option code):

generic printing options

  • activate single line printing mode:
inline_mode: bool = false
  • limit the printing depth (0 does not limit):
max_depth: u8 = 10
  • specify depths to include or exclude from the output:
filter_depths: Filter(usize) = .{ .exclude = &.{} }
  • indentation size for multi-line printing mode:
tab_size: u8 = 2
  • add extra empty line at the end of print (to stack up multiple prints):
print_extra_empty_line: bool = false
  • indicate empty output with a message (otherwise empty output length is 0):
indicate_empty_output: bool = true
  • specify a custom format string (eg. pre{s}post) to surround the resulting output:
fmt: []const u8 = ""

type printing options

  • show type tags (ie. std.builtin.TypeId, such as .Union, .Int):
show_type_tags: bool = false
  • show type names:
show_type_names: bool = true
  • limit the length of type names (0 does not limit):
type_name_max_len: usize = 60
  • specify depth of folding parentheses in type names (0 does not fold):
type_name_fold_parens: usize = 1
  • refine depth of folding parentheses in function signatures (0 does not fold):
type_name_fold_parens_fn: usize = 2
  • refine depth of folding parentheses for special case @TypeOf(..) (0 does not fold):
type_name_fold_parens_type_of: usize = 2

value printing options

  • show values:
show_vals: bool = true
  • show empty values:
show_empty_vals: bool = true

pointer printing options

  • follow pointers instead of printing their address:
ptr_deref: bool = true
  • reduce duplicating depths when dereferencing pointers:
ptr_skip_dup_unfold: bool = true
  • treat [*:sentinel]T as array (except [*:0]u8, see .ptr_many_u8z_is_str instead:
ptr_many_with_sentinel_is_array: bool = true

optional printing options

  • reduce duplicating depths when unfolding optional types:
optional_skip_dup_unfold: bool = true

struct and union printing options

  • show struct fields:
struct_show_field_names: bool = true
  • treat empty structs as having (empty) value:
struct_show_empty: bool = true
  • inline primitive type values to save vertical space:
struct_inline_prim_types: bool = true
  • limit the number of fields in the output (0 does not limit):
struct_max_len: usize = 15
  • specify field names to include or exclude from the output:
filter_field_names: Filter([]const u8) = .{ .exclude = &.{} }
  • specify field type tags to include or exclude from the output:
filter_field_type_tags: Filter(std.builtin.TypeId) = .{ .exclude = &.{} }
  • specify field types to include or exclude from the output:
filter_field_types: Filter(type) = .{ .exclude = &.{} }

array and slice printing options

  • limit the number of items in the output (0 does not limit):
array_max_len: usize = 20
  • show item indices:
array_show_item_idx: bool = true
  • inline primitive types to save vertical space:
array_inline_prim_types: bool = true
  • show primitive types' type information (name and tag):
array_show_prim_type_info: bool = false

primitive type printing options

  • specify type tags to treat as primitives:
prim_type_tags: Filter(std.builtin.TypeId) = .{ .include = &.{
    .Int,
    .ComptimeInt,
    .Float,
    .ComptimeFloat,
    .Void,
    .Bool,
} }
  • specify concrete types to treat as primitives:
prim_types: Filter(type) = .{ .include = &.{} }

string printing options

  • limit the length of strings (0 does not limit):
str_max_len: usize = 80
  • treat []u8 as "string":
slice_u8_is_str: bool = true
  • treat [:0]u8 as "string":
slice_u8z_is_str: bool = true
  • treat [n]u8 as "string":
array_u8_is_str: bool = false
  • treat [n:0]u8 as "string":
array_u8z_is_str: bool = true
  • treat [*:0]u8 as "string":
ptr_many_u8z_is_str: bool = true

examples

derived from src/tests.zig:

const value: struct {
    field1: bool = true,
    field2: u8 = 42,
    field3: f32 = 1.1,
} = .{};
try pretty.printInline(alloc, value, .{});
file.main__struct_1472{ .field1: bool = true, .field2: u8 = 42, .field3: f32 = 1.1e0 }
try pretty.print(alloc, value, .{});
file.main__struct_1652
  .field1: bool => true
  .field2: u8 => 42
  .field3: f32 => 1.1e0

use filter_* options to "query" or cut off unnecessary output:

try pretty.print(alloc, value, .{
    .filter_depths = .{ .exclude = &.{ 0, 2 } },
});
.field1: bool
.field2: u8
.field3: f32
try pretty.print(alloc, value, .{
      .filter_depths = .{ .include = &.{2} },
});
true
42
1.1e0
try pretty.print(alloc, value, .{
    .filter_depths = .{ .exclude = &.{0} },
    .filter_field_names = .{ .include = &.{"field2"} },
});
.field2: u8 => 42

contributing ❤️

please feel free to:

  • open an issue and describe the change you'd like to see.
  • fork the repository and submit your pull request.

license

all codebase are belong to MIT.