import * as comma from "add-commas"; import * as _ from "lodash"; import * as markdownTable from "markdown-table"; import * as yargs from "yargs"; import * as scanner from "../scanner"; export var command = "table"; export var describe = "Create a summary table of requested fields"; export function builder(yargs: yargs.Arguments) { return yargs .help("help") .alias("f", "fields") .array("fields") .default("fields", ["_basename", "title"]) .alias("t", "titles") .array("titles") .default("table-start", "| ") .default("table-end", " |") .default("table-delimiter", " | ") .boolean("table-rule") .default("table-rule", true) .boolean("table-header") .default("table-header", true) .default("list-delimiter", ", ") .alias("o", "output") .default("output", "-") .default("prefix", "") .demand(1); } export function handler(argv: any) { var files = argv._.splice(1); var data = scanner.scanFiles(argv, files); render(argv, data); } export function render(argv, data) { // Parse out the options and fields from the sources. var columns = parse(argv, argv.titles, argv.fields); // Create the header row. var header: any[] = []; for (var column1 of columns) { header.push(column1.header); } // Create the initial table with the header. var table = [header]; // Loop through the results and get the fields we need to display. var totals: any = ["Totals"]; for (var metadata of data) { // Add the row to the table. var row: any[] = []; table.push(row); // Loop through our fields and retrieve each one. for (var index = 0; index < columns.length; index++) { // Grab the value, even if nested. var column = columns[index]; var value = _.get(metadata, column.ref); // If we have a list, format it with spaces. if (Array.isArray(value)) { value = value.join(argv.listDelimiter); } // If we have totals, then add them. if (column.total) { totals[index] = totals[index] ? parseInt(totals[index]) : 0; totals[index] += parseInt(value); } // If we have commas requested, then add those. if (column.comma) { value = comma(value); } // Add the final row to the table. row.push(value); } } // If we have totals, then add it at the bottom. if (totals.length > 1) { for (var index2 = 0; index2 < columns.length && index2 < totals.length; index2++) { var column2 = columns[index2]; if (column2.comma) { totals[index2] = comma(totals[index2]); } } table.push(totals); } // Format the results in a table. var formattedTable = markdownTable(table, { align: columns.map(c => c.align), delimiter: argv.tableDelimiter, start: argv.tableStart, end: argv.tableEnd, rule: argv.tableRule }); // If we don't want the header row, strip off the first line. if (!argv.tableHeader) { formattedTable = formattedTable.substring(formattedTable.indexOf("\n") + 1); } console.log(formattedTable); } class Column { ref: string; header: string; align: string = "l"; comma: boolean = false; total: boolean = false; public set(field, header) { this.ref = this.parseSpecifier(field); this.header = this.parseSpecifier(header ? header : field); } private parseSpecifier(spec): string { // See if we have options. var m = spec.match(/^(.*?):([lcr.st]+)?$/); if (m) { // We have a match, so put the first part as the specifier. spec = m[1]; for (var s of m[2]) { switch (s) { case "c": case "l": case "r": case ".": this.align = s; break; case "s": this.comma = true; break; case "t": this.total = true; break; } } } // Return the resulting specifier. return spec; } } function parse(argv, titles, fields): Column[] { var columns: Column[] = []; if (!titles) titles = []; for (var index = 0; index < fields.length; index++) { var column = new Column(); column.set( fields[index], titles.length >= index ? titles[index] : undefined); columns.push(column); } return columns; } /* Notes Comma-separated list of lists. I like spaces. */