Skip to content
This repository was archived by the owner on Jul 14, 2025. It is now read-only.

Commit b4d7f20

Browse files
committed
Cope with spaces in column header and body
1 parent a364e83 commit b4d7f20

File tree

2 files changed

+88
-6
lines changed

2 files changed

+88
-6
lines changed

ts/src/table.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,51 @@ export function asTableLines(output: KubectlOutput): Errorable<TableLines> {
5858
return { succeeded: false, reason: 'kubectl-error', error: output.stderr };
5959
}
6060

61+
interface TableColumn {
62+
readonly name: string;
63+
readonly startIndex: number;
64+
readonly endIndex?: number;
65+
}
66+
6167
function parseTableLines(table: TableLines, columnSeparator: RegExp): Dictionary<string>[] {
6268
if (table.header.length === 0 || table.body.length === 0) {
6369
return [];
6470
}
65-
const columnHeaders = table.header.toLowerCase().replace(columnSeparator, '|').split('|');
66-
return table.body.map((line) => parseLine(line, columnHeaders, columnSeparator));
71+
const columns = parseColumns(table.header, columnSeparator);
72+
return table.body.map((line) => parseLine(line, columns));
6773
}
6874

69-
function parseLine(line: string, columnHeaders: string[], columnSeparator: RegExp) {
75+
function parseLine(line: string, columns: TableColumn[]) {
7076
const lineInfoObject = Dictionary.of<string>();
71-
const bits = line.replace(columnSeparator, '|').split('|');
72-
bits.forEach((columnValue, index) => {
73-
lineInfoObject[columnHeaders[index].trim()] = columnValue.trim();
77+
columns.forEach((column) => {
78+
const text = line.substring(column.startIndex, column.endIndex).trim();
79+
lineInfoObject[column.name] = text;
7480
});
7581
return lineInfoObject;
7682
}
83+
84+
function parseColumns(columnHeaders: string, columnSeparator: RegExp): TableColumn[] {
85+
const columnStarts = parseColumnStarts(columnHeaders, columnSeparator);
86+
const columns = Array.of<TableColumn>();
87+
columnStarts.forEach((column, index) => {
88+
const endIndex = (index < columnStarts.length - 1) ?
89+
columnStarts[index + 1].startIndex - 1 :
90+
undefined;
91+
columns.push({ endIndex, ...column });
92+
});
93+
return columns;
94+
}
95+
96+
function parseColumnStarts(columnHeaders: string, columnSeparator: RegExp) {
97+
const columns = Array.of<TableColumn>();
98+
const columnNames = columnHeaders.replace(columnSeparator, '|').split('|');
99+
let takenTo = 0;
100+
for (const columnName of columnNames) {
101+
const startIndex = columnHeaders.indexOf(columnName, takenTo);
102+
if (startIndex >= 0) {
103+
takenTo = startIndex + columnName.length;
104+
columns.push({ name: columnName.toLowerCase(), startIndex });
105+
}
106+
}
107+
return columns;
108+
}

ts/test/table.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ foo true false
1111
barbar false twice
1212
`.trim();
1313

14+
const KUBECTL_SAMPLE_GET_WIDE_RESULT =
15+
`
16+
NAMESPACE NAME FOO BAR RELEASE STATUS SPLINE LEVEL
17+
ns1 foo true false green reticulated
18+
ns2 barbar false twice dark orange none
19+
`.trim();
20+
21+
const KUBECTL_SAMPLE_MULTISPACE_RESULT =
22+
`
23+
NAMESPACE NAME FOO BAR RELEASE STATUS MOTTO
24+
ns1 foo true false green let the games begin
25+
ns2 barbar false twice dark orange none
26+
`.trim();
27+
1428
describe('asTableLines', () => {
1529
it('should report failure if kubectl failed to run', () => {
1630
const result = parser.asTableLines(undefined);
@@ -84,4 +98,40 @@ describe('parseTabular', () => {
8498
assert.equal(objects[1].foo, 'false');
8599
assert.equal(objects[1].bar, 'twice');
86100
});
101+
it('should parse headers with spaces correctly', () => {
102+
const result = parser.parseTabular({ code: 0, stdout: KUBECTL_SAMPLE_GET_WIDE_RESULT, stderr: '' });
103+
assert.equal(true, result.succeeded);
104+
const objects = (<Succeeded<Dictionary<string>[]>>result).result;
105+
assert.equal(objects.length, 2);
106+
assert.equal(objects[0].namespace, 'ns1');
107+
assert.equal(objects[0].name, 'foo');
108+
assert.equal(objects[0].foo, 'true');
109+
assert.equal(objects[0].bar, 'false');
110+
assert.equal(objects[0]['release status'], 'green');
111+
assert.equal(objects[0]['spline level'], 'reticulated');
112+
assert.equal(objects[1].namespace, 'ns2');
113+
assert.equal(objects[1].name, 'barbar');
114+
assert.equal(objects[1].foo, 'false');
115+
assert.equal(objects[1].bar, 'twice');
116+
assert.equal(objects[1]['release status'], 'dark orange');
117+
assert.equal(objects[1]['spline level'], 'none');
118+
});
119+
it('should parse lines with multiple spaces correctly', () => {
120+
const result = parser.parseTabular({ code: 0, stdout: KUBECTL_SAMPLE_MULTISPACE_RESULT, stderr: '' });
121+
assert.equal(true, result.succeeded);
122+
const objects = (<Succeeded<Dictionary<string>[]>>result).result;
123+
assert.equal(objects.length, 2);
124+
assert.equal(objects[0].namespace, 'ns1');
125+
assert.equal(objects[0].name, 'foo');
126+
assert.equal(objects[0].foo, 'true');
127+
assert.equal(objects[0].bar, 'false');
128+
assert.equal(objects[0]['release status'], 'green');
129+
assert.equal(objects[0]['motto'], 'let the games begin');
130+
assert.equal(objects[1].namespace, 'ns2');
131+
assert.equal(objects[1].name, 'barbar');
132+
assert.equal(objects[1].foo, 'false');
133+
assert.equal(objects[1].bar, 'twice');
134+
assert.equal(objects[1]['release status'], 'dark orange');
135+
assert.equal(objects[1]['motto'], 'none');
136+
});
87137
});

0 commit comments

Comments
 (0)