Skip to content

Commit d4fac34

Browse files
committed
feat(dataformat): add key-value format
1 parent 7af4092 commit d4fac34

5 files changed

Lines changed: 91 additions & 1 deletion

File tree

.github/workflows/master.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- uses: actions/checkout@v1
17+
- uses: actions/setup-node@v3
18+
with:
19+
node-version: 18
1720
- uses: actions/cache@v1
1821
with:
1922
path: ~/.npm

src/dataformat/csv.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ function readCSVLine(chars: string[], startAt: number, delimiter: string = ','):
2929
}
3030
} else if (ch === '\\') {
3131
buffer.push(chars[++i]);
32-
continue;
3332
} else if (ch === delimiter && currentQuote === undefined) {
3433
values.push(buffer.join(''));
3534
buffer = [];

src/dataformat/kv.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { DataFormat } from './domain';
2+
3+
function writeAsKVSync(object: unknown, delimiter: string = ' '): string {
4+
if (typeof object === 'string') return object;
5+
return Object.entries(object)
6+
.map(([key, value]) => `${key}='${value.replace(/'/g, "\\'")}'`)
7+
.join(delimiter);
8+
}
9+
function parseKVSync<T>(content: string, delimiter: string = ' '): T {
10+
const chars = content.split('');
11+
const accumulator: { [key: string]: string } = {};
12+
let keybuffer: string[] = [];
13+
let valuebuffer: string[] = [];
14+
let buffer = keybuffer;
15+
let currentQuote: string | undefined = undefined;
16+
17+
for (let i = 0; i < chars.length; i++) {
18+
const ch = chars[i];
19+
if (ch === '"' || ch === "'") {
20+
if (currentQuote === undefined) {
21+
currentQuote = ch;
22+
} else if (ch === currentQuote) {
23+
currentQuote = undefined;
24+
} else {
25+
buffer.push(ch);
26+
}
27+
} else if (ch === '\\') {
28+
buffer.push(chars[++i]);
29+
} else if (ch === '=' && currentQuote === undefined) {
30+
buffer = valuebuffer;
31+
} else if ((ch === delimiter && currentQuote === undefined) || (ch === '\n' && currentQuote === undefined)) {
32+
if (keybuffer.length > 0) {
33+
accumulator[keybuffer.join('')] = valuebuffer.join('');
34+
}
35+
keybuffer = [];
36+
valuebuffer = [];
37+
buffer = keybuffer;
38+
} else {
39+
buffer.push(ch);
40+
}
41+
}
42+
if (buffer.length > 0) {
43+
if (keybuffer.length > 0) {
44+
accumulator[keybuffer.join('')] = valuebuffer.join('');
45+
}
46+
}
47+
48+
return accumulator as unknown as T;
49+
}
50+
51+
export const KvFormat: DataFormat<unknown> = {
52+
serialize(object: Array<string[]>, delimiter: string = ' '): string {
53+
return writeAsKVSync(object, delimiter);
54+
},
55+
deserialize(content: string, delimiter: string = ' '): Array<string[]> {
56+
return parseKVSync(content, delimiter);
57+
}
58+
};

test/dataformat/kv.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { test, assertThat } from '../test-lib';
2+
import { KvFormat } from '../../src/dataformat/kv';
3+
4+
const content = `
5+
action='CREATE' subject='A123456' resource='resource.name' RESOURCE_ID='1234' META='NAME'
6+
action=DELETE subject=A123456 resource=resource.name RESOURCE_ID=6547 META="NAME ADR, OK"
7+
action='READ' subject='A123456' resource='resource.name' RESOURCE_ID='7895' META='NESTEST=value'
8+
`
9+
.trim()
10+
.split('\n');
11+
12+
test('should be able to parse keyvalue string', () => {
13+
const result = content.map((it) => KvFormat.deserialize(it));
14+
assertThat(
15+
{ action: 'CREATE', subject: 'A123456', resource: 'resource.name', RESOURCE_ID: '1234', META: 'NAME' },
16+
result[0],
17+
'audit log should match'
18+
);
19+
assertThat(
20+
{ action: 'DELETE', subject: 'A123456', resource: 'resource.name', RESOURCE_ID: '6547', META: 'NAME ADR, OK' },
21+
result[1],
22+
'kvlog without quotes should word'
23+
);
24+
assertThat(
25+
{ action: 'READ', subject: 'A123456', resource: 'resource.name', RESOURCE_ID: '7895', META: 'NESTEST=value' },
26+
result[2],
27+
'nested kv-values should work'
28+
);
29+
});

test/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
import './dataformat/csv.test';
22
import './dataformat/json.test';
3+
import './dataformat/kv.test';

0 commit comments

Comments
 (0)