Skip to content

Commit

Permalink
util: design and implement Console.table to work with Fantom types
Browse files Browse the repository at this point in the history
  • Loading branch information
briansfrank committed Jun 18, 2024
1 parent de16d4e commit dfa1662
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 8 deletions.
20 changes: 19 additions & 1 deletion src/util/es/Console.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,25 @@ class Console extends sys.Obj {

err(msg) { console.error(msg); return this; }

table(obj) { console.table(obj); return this; }
table(obj)
{
var grid = []
var t = fan.util.ConsoleTable.make(obj);
for (var r=0; r<t.rows().size(); ++r)
{
var row = t.rows().get(r);
var obj = {};
for (var c=0; c<t.headers().size(); ++c)
{
var key = t.headers().get(c);
var val = row.get(c);
obj[key] = val;
}
grid.push(obj);
}
console.table(grid)
return this;
}

group(msg, collapsed)
{
Expand Down
132 changes: 130 additions & 2 deletions src/util/fan/Console.fan
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ native const final class Console
** Print a message to the console at the error level
This err(Obj? msg)

** Print tabular data to the console.
** TODO: more docs around how lists and maps work...
** Print tabular data to the console:
** - List of list is two dimensional data where first row is header names
** - List of items with an each method will create column per key
** - List of items without each will map to a column of "val"
** - Anything else will be table of one cell table
This table(Obj? obj)

** Clear the console of all text if supported
Expand All @@ -60,3 +63,128 @@ native const final class Console
Str? promptPassword(Str msg := "")
}

**************************************************************************
** ConsoleTable
**************************************************************************

**
** ConsoleTable is helper class to coerce objects to tables
**
@NoDoc @Js
class ConsoleTable
{
new make(Obj? x)
{
list := x as List

// list of lists
if (list != null && list.first is List)
{
headers = list[0]
if (list.size > 1) rows = list[1..-1]
return
}

// list of something
if (list != null)
{
// turn each item in list to a Str:Str map
maps := Str:Str[,]
list.each |item| { maps.add(map(item)) }

// create list of columns union
cols := Str:Str[:] { ordered = true }
maps.each |map|
{
map.each |v, k| { cols[k] = k }
}
headers = cols.vals

// now turn each row Str:Str into a Str[] of cells
maps.each |map|
{
row := Str[,]
row.capacity = headers.size
cols.each |k| { row.add(map[k] ?: "") }
rows.add(row)
}
return
}

// scalar value
headers = ["val"]
rows = [[str(x)]]
}

Str[] headers := [,]
Str[][] rows := [,]

once Int[] widths()
{
widths := Int[,]
widths.capacity = headers.size
headers.each |h, c|
{
w := h.size
rows.each |row|
{
w = w.max(row[c].size)
}
widths.add(w)
}
return widths
}

Void dump(Console c)
{
c.info(row(headers))
c.info(underlines)
rows.each |x| { c.info(row(x)) }
}

private Str row(Str[] cells)
{
s := StrBuf()
cells.each |cell, i|
{
if (i > 0) s.add(" ")
s.add(cell)
s.add(Str.spaces(widths[i] - cell.size))
}
return s.toStr
}

private Str underlines()
{
s := StrBuf()
headers.each |h, i|
{
if (i > 0) s.add(" ")
widths[i].times { s.addChar('-') }
}
return s.toStr
}

static Str:Str map(Obj? x)
{
if (x == null) return ["val":"null"]

m := x.typeof.method("each", false)
if (m == null || x is Str) return ["val":str(x)]

acc := Str:Str[:] { ordered = true }
f := |v, k| { acc[str(k)] = str(v)}
m.callOn(x, [f])
return acc
}

private static Str str(Obj? x)
{
if (x == null) return "null"
s := x.toStr
if (s.contains("\n")) s = s.splitLines.first
if (s.size > 80) s = s[0..80] + ".."
return s
}
}

4 changes: 2 additions & 2 deletions src/util/java/Console.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,15 @@ private Console log(String level, Object msg)

public Console table(Object obj)
{
info("TODO: " + obj);
ConsoleTable.make(obj).dump(this);
return this;
}

public Console group(Object obj) { return group(obj, false); }
public Console group(Object obj, boolean collapse)
{
indent++;
info(obj);
indent++;
return this;
}
private int indent;
Expand Down
20 changes: 19 additions & 1 deletion src/util/js/Console.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,25 @@ fan.util.Console.prototype.width = function() { return null; }

fan.util.Console.prototype.height = function() { return null; }

fan.util.Console.prototype.table = function(obj) { console.table(obj); return this; }
fan.util.Console.prototype.table = function(obj)
{
var grid = []
var t = fan.util.ConsoleTable.make(obj);
for (var r=0; r<t.rows().size(); ++r)
{
var row = t.rows().get(r);
var obj = {};
for (var c=0; c<t.headers().size(); ++c)
{
var key = t.headers().get(c);
var val = row.get(c);
obj[key] = val;
}
grid.push(obj);
}
console.table(grid)
return this;
}

fan.util.Console.prototype.group = function(msg, collapsed)
{
Expand Down
31 changes: 29 additions & 2 deletions src/util/test/ConsoleTest.fan
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,35 @@ class ConsoleTest : Test
c.warn("Warn message!")
c.err("Error message!")

// tables
c.table("scalar")
// table - null
c.info("")
c.table(null)

// table - scalar
c.info("")
c.table(123)

// table - list of scalars
c.info("")
c.table(["a", "b", "c"])

// table - list of maps
c.info("")
c.table([
["First Name":"Bob", "Last Name":"Smith"],
["First Name":"John", "Last Name":"Apple", "Approx Age":52],
["First Name":"Alice", "Last Name":"Bobby", "Job":"Programmer"],
])

// table - 2d grid
c.info("")
c.table([
["Name", "Age", "Hire Month"],
["Alpha", "30", "Jan-2020"],
["Beta", "40", "Feb-1996"],
["Charlie", "50", "Mar-2024"]
])
c.info("")

// group
c.group("indent 0")
Expand Down

0 comments on commit dfa1662

Please sign in to comment.