Skip to content

Commit

Permalink
Merge pull request #11 from evert/serialize
Browse files Browse the repository at this point in the history
Serialize
  • Loading branch information
evert committed Apr 27, 2019
2 parents f7612c6 + 033818b commit 2e1be6c
Show file tree
Hide file tree
Showing 21 changed files with 415 additions and 61 deletions.
40 changes: 0 additions & 40 deletions index.js

This file was deleted.

33 changes: 32 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,15 @@
"chai": "^4.2.0",
"mocha": "^6.1.4",
"nyc": "^14.0.0",
"ts-node": "^8.1.0",
"tslint": "^5.16.0",
"typescript": "^3.4.5",
"webpack": "^4.30.0",
"webpack-cli": "^3.3.1"
},
"nyc": {
"extension": [
".ts"
]
}
}
77 changes: 77 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import Parser from './parser';
import Serializer from './serializer';
import { Dictionary, Item, List, ListList, ParameterizedList } from './types';

module.exports = {

parseDictionary: (input: string): Dictionary => {

const parser = new Parser(input);
return parser.parseDictionary();

},

parseList: (input: string): List => {

const parser = new Parser(input);
return parser.parseList();

},

parseListList: (input: string): ListList => {

const parser = new Parser(input);
return parser.parseListList();

},

parseParamList: (input: string): ParameterizedList => {

const parser = new Parser(input);
return parser.parseParamList();

},

parseItem: (input: string): Item => {

const parser = new Parser(input);
return parser.parseItem();

},

serializeDictionary: (input: Dictionary): string => {

const serializer = new Serializer();
return serializer.serializeDictionary(input);

},

serializeList: (input: List) => {

const serializer = new Serializer();
return serializer.serializeList(input);

},

serializeListList: (input: ListList) => {

const serializer = new Serializer();
return serializer.serializeListList(input);

},

serializeParamList: (input: ParameterizedList) => {

const serializer = new Serializer();
return serializer.serializeParamList(input);

},

serializeItem: (input: Item) => {

const serializer = new Serializer();
return serializer.serializeItem(input);

},

};
14 changes: 2 additions & 12 deletions src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
type Item = string | number | Buffer | boolean;
import { Dictionary, Item, List, ListList, ParameterizedIdentifier, ParameterizedList } from './types';

type Dictionary = {
[s: string]: Item
};

type List = Item[];
type ListList = List[];

type ParameterizedIdentifier = [string, Dictionary];
type ParameterizedList = ParameterizedIdentifier[];

class Parser {
export default class Parser {

input: string;
position: number;
Expand Down
127 changes: 127 additions & 0 deletions src/serializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { Dictionary, Item, List, ListList, ParameterizedList } from './types';

export default class Serializer {

serializeDictionary(input: Dictionary): string {

const output = [];
for (const [key, value] of Object.entries(input)) {
output.push(this.serializeKey(key) + '=' + this.serializeItem(value));
}

return output.join(', ');

}

serializeList(input: List): string {

return input.map(this.serializeItem.bind(this)).join(', ');

}

serializeListList(input: ListList): string {

return input.map(
innerList => innerList.map(this.serializeItem.bind(this)).join('; ')
).join(', ');

}

serializeParamList(input: ParameterizedList): string {

const output = [];
for (const [key, dict] of input) {
let item = '';
item += this.serializeKey(key);

for (const [dictKey, dictValue] of Object.entries(dict)) {
item += ';' + this.serializeKey(dictKey);
if (dictValue) {
item += '=' + this.serializeItem(dictValue);
}
}

output.push(item);
}
return output.join(', ');

}

serializeKey(input: string): string {

if (!/^[a-z][a-z0-9_-]*$/.test(input)) {
throw new Error('Dictionary keys must start with a-z and only contain a-z0-9_-');
}

return input;

}

serializeItem(input: Item): string {

if (typeof input === 'number') {
if (Number.isInteger(input)) {
return this.serializeInt(input);
}
return this.serializeFloat(input);
}
if (typeof input === 'string') {
return this.serializeString(input);
}
// Token ???
if (typeof input === 'boolean') {
return this.serializeBoolean(input);
}
if (input instanceof Buffer) {
return this.serializeByteSequence(input);
}
throw new Error('Cannot serialize values of type ' + typeof input);

}

serializeInt(input: number): string {
if (input > 999_999_999_999_999 || input < -999_999_999_999_999) {
throw new Error('Integers may not be larger than 15 digits');
}
return input.toString();
}

serializeFloat(input: number): string {

return input.toString();

}

serializeString(input: string): string {

if (!/^[\x1F-\x7F]*$/.test(input)) {
throw new Error('Strings must be in the ASCII range');
}

return '"' + input.replace('"', '\\"') + '"';

}

serializeToken(input: string): string {

if (!/^[a-zA-Z][a-zA-Z0-9_\-\.\:\%\*]*$/.test(input)) {
throw new Error('Tokens must start with a letter and must only contain A-Za-z_-.:%*/');
}

return input;

}

serializeByteSequence(input: Buffer): string {

return '*' + input.toString('base64') + '*';

}

serializeBoolean(input: boolean): string {

return input ? '?1' : '?0';

}

}
11 changes: 11 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type Item = string | number | Buffer | boolean;

export type Dictionary = {
[s: string]: Item
};

export type List = Item[];
export type ListList = List[];

export type ParameterizedIdentifier = [string, Dictionary];
export type ParameterizedList = ParameterizedIdentifier[];
4 changes: 2 additions & 2 deletions test/httpwg-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ function makeTest(test) {
const parser = new Parser(test.raw.join(','));

const skipped = [
'too long integer',
'negative too long integer',
'long integer',
'long negative integer',
];

it(test.name, function() {
Expand Down
4 changes: 4 additions & 0 deletions test/mocha.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
--recursive
--require ts-node/register
--exit
test/*.js
2 changes: 1 addition & 1 deletion test/parse-dictionary.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const parse = require('../index').parseDictionary;
const parse = require('../dist').parseDictionary;
const expect = require('chai').expect;

describe("Dictionaries", () => {
Expand Down
2 changes: 1 addition & 1 deletion test/parse-item.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const parse = require('../index').parseItem;
const parse = require('../dist').parseItem;
const expect = require('chai').expect;

describe("Lists", () => {
Expand Down
2 changes: 1 addition & 1 deletion test/parse-list-list.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const parse = require('../index').parseListList;
const parse = require('../dist').parseListList;
const expect = require('chai').expect;

describe("Lists of lists", () => {
Expand Down
Loading

0 comments on commit 2e1be6c

Please sign in to comment.