Git Product home page Git Product logo

texttable's People

Contributors

cfilipov avatar mattpolzin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

texttable's Issues

Cocoapods support

I've been keeping an eye on this repo for ages, because it's a nice fit for some console debug output I need for histograms. Today was the day I decided to put it in action, then I realised there's no pod.

I'm reading up on Swift's packager manager now, but it's so far not as straight forward (from my perspective) as Cocoapods, so it's a hindrance. Being able to just add another line to my Podfile would be a plus.

That aside, loving the design of this lib!

Proposed API breaking changes

This proposal supersedes #1, which was tentatively abandoned. This proposal is a breaking API change predicated on the approval of improved key paths in swift SE-0161.

New Text Table Creation

I propose a new API for constructing a TextTable instance. In particular, the way column definitions are mapped to values. Column/value mapping will be defined via a dictionary of key paths to columns.

Current API

The current API uses an initializer that takes a closure which is expected to return an array of column definitions given an instance of some row type Person.

let table = TextTable<Person> {
    [Column("Name" <- $0.name),
     Column("Age" <- $0.age),
     Column("Birthday" <- $0.birthday)]
}

This has a few disadvantages:

  1. We can't get the column definition without a value passed to the closure, so a dummy value (the instance for the 0th row) is passed to get the column names.
  2. When computing column widths we actually want to traverse all the values of one column at a time, but we are forced to get the whole row each time.
  3. The column definition itself loses the type information of the value, storing it in an Any.

New API

With the new API, TextTable will conform to ExpressibleByDictionaryLiteral. A TextTable<T> will be created by using a dictionary literal of [PartialKeyPath<T>: Column].

Questions

  1. Should Column be Column<T>?
  2. Could/should we use KeyPath<T, V> instead?
let table: TextTable<Person> = [
    .name: "Name",
    .country: "Country",
    .visited: "Last Visit"
]

The above snippet of code is just short hand for the following (Column will conform to ExpressibleByStringLiteral, so Column does not have to be explicitly constructed):

let table: TextTable<Person> = [
    .name: Column("Name"),
    .country: Column("Country"),
    .visited: Column("Last Visit")
]

The above snippet is also short hand for the following code. This example illustrates the default values that are used when you leave out the optional arguments. Typically you would only provide arguments where you want to customize the specific column.

let table: TextTable<Person> = [
    .name: Column("Name", width: .auto, align: .auto, truncate: .tail, formatter: nil),
    .country: Column("Country", width: .auto, align: .auto, truncate: .tail, formatter: nil),
    .visited: Column("Last Visit", width: .auto, align: .auto, truncate: .tail, formatter: nil)
]

You would never type this out, but for illustration purposes here is an even more explicit version. Once again, this is equivalent to all the previous examples:

let table: TextTable<Person> = [
    #keyPath(Person, .name): Column("Name", width: .auto, align: .auto, truncate: .tail, formatter: nil),
    #keyPath(Person, .country): Column("Country", width: .auto, align: .auto, truncate: .tail, formatter: nil),
    #keyPath(Person, .visited): Column("Last Visit", width: .auto, align: .auto, truncate: .tail, formatter: nil)
]

String Constructor Instead of String Method

Another change to consider: Extending String with an init for TextTable instead of using a string(for:) method.

let s = String(table: table)

Instead of

let s = table.string(for: data)

Stream Print

TextTable tries to be memory efficient. It will not copy the contents of your collection, instead it will call back to your collection whenever it needs a value.

Currently the print method on table just creates a string using the aforementioned API and just passes it to Swift.print. The next version will instead stream the contents of the table to Swift.print roughly line-by-line as the table is generated.

Conformance to Sequence Type

TextTable will actually conform to swift's Collection (or maybe just provide a Sequence). This will let one lazily pull the string parts of the rendered table.

Upcoming Changes

The API of this lib has seen several major changes. But it has been converging on what I think resembles a good API.

New Text Table Creation

As of v1.0.0-alpha.4 this is how you create a text table:

let table = TextTable<Person> {
    [Column("Name" <- $0.name),
     Column("Age" <- $0.age),
     Column("Birthday" <- $0.birhtday)]
}

This has one major disadvantage:

The closure that maps the type to a column value is intertwined with the rest of the column definition. You can't get the column definition without calling the closure, and you can't call the closure unless you pass in a value. This is problematic when you need column information up front, before the first row is even processed. The callback ends up calling the closure using the first row multiple times just to get the column definition.

I propose the next version will have the following API instead:

let table: TextTable<Person> = [
    { $0.name } <- Column("Name"),
    { $0.country } <- Column("Country"),
    { $0.visited } <- Column("Last Visit")
]

Since the example above didn't customize the columns in any way, the following is also valid:

let table: TextTable<Person> = [
    { $0.name } <- "Name",
    { $0.country } <- "Country",
    { $0.visited } <- "Last Visit"
]

The goal is that this library will have good-enough defaults that for most cases one wouldn't be compelled to customize each column.

The <- operator is just syntactic sugar that allows us to keep the property mapping visually close to the column name so it reads better. The following is equivalent to the previous two examples:

let table: TextTable<Person> = [
    Column("Name", valueMapping: { $0.name }),
    Column("Country", valueMapping: { $0.country }),
    Column("Last Visit", valueMapping: { $0.visited })
]

Note the differences to the current API:

  1. TextTable<T> now conforms to ExpressibleByArrayLiteral. Instead of passing a closure on the init method of TextTable, you are now passing an array literal of columns.
  2. The value mapping for the column is defined as an individual closure for each column instead of a single closure for all columns.

Customizing the columns works similar to the current version:

let table: TextTable<Person> = [
    { $0.name } <- Column("Name", align: .left),
    { $0.country } <- Column("Country", align: .center, width: 12, truncate: .head),
    { $0.visited } <- Column("Last Visit", align: .right, formatter: df)
]

String Constructor Instead of String Method

Another change to consider: Extending String with an init for TextTable instead of using a string(for:) method.

let s = String(table: table)

Instead of

let s = table.string(for: data)

Stream Print

TextTable tries to be memory efficient. It will not copy the contents of your collection, instead it will call back to your collection whenever it needs a value.

Currently the print method on table just creates a string using the aforementioned API and just passes it to Swift.print. The next version will instead stream the contents of the table to Swift.print roughly line-by-line as the table is generated.

Conformance to Sequence Type

TextTable will actually conform to swift's Collection (or maybe just provide a Sequence). This will let one lazily pull the string parts of the rendered table.

Support colors in table cells

Hi,

I would like to use https://github.com/onevcat/Rainbow to colorize cells in a TextTable.

let table = TextTable<Attributes> {
    [   
        Column(title: "Country", value: $0.country!.blue), 
        Column(title: "County", value: $0.county!.red),
    ]   
}   
table.print(result, style: Style.psql)

This result in a table where the length of the cell data in https://github.com/cfilipov/TextTable/blob/master/Sources/TextTable/TextTable.swift#L237 should only count the visible characters. The escape sequences used for colors should not be counted here:

+----------------------------+--------------------------------+
| Country                    | County                         |
+----------------------------+--------------------------------+
| Baden-Württemberg | SK Karlsruhe          |
| Baden-Württemberg | SK Ulm                |

(The colors are missing...)

Is there any way this can already be achieved or implemented?

Greetings
Joachim

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.