diff --git a/classic-api.ts b/classic-api.ts index 65c7d53..f0a3fa3 100644 --- a/classic-api.ts +++ b/classic-api.ts @@ -13,7 +13,7 @@ type Writer = ((data: T) => void) | { write: (data: T) => void }; * @param writer writer to identify output function for */ const getWriteFunc = (writer: Writer) => - "write" in writer ? writer.write.bind(writer) : writer; + "write" in writer ? writer.write.bind(writer) : writer; /** * Take a function and an object and produce a function with properties. @@ -21,18 +21,20 @@ const getWriteFunc = (writer: Writer) => * @param obj an object containing properties to copy to the function */ const enrichFunction = (func: F, obj: O): F & O => - Object.assign(func, obj); + Object.assign(func, obj); /** * A `Ratlogger` is a function that takes the components of a `RatlogData` and * does something with it. It also exposes a `tag` property-function which produces * an identical `Ratlogger` with the added tags. */ -export type Ratlogger = (( - message: Stringable, - fields?: RatlogData["fields"], - ...tags: Stringable[] -) => void) & { tag: (...tags: Stringable[]) => Ratlogger }; +export type Ratlogger = + & (( + message: Stringable, + fields?: RatlogData["fields"], + ...tags: Stringable[] + ) => void) + & { tag: (...tags: Stringable[]) => Ratlogger }; /** * Constructor for more customizable logger instances. @@ -40,55 +42,55 @@ export type Ratlogger = (( * @param tags a list of tags to apply to every output from this logger */ const generateRatlogger = ( - writer: Writer, - ...tags: Stringable[] + writer: Writer, + ...tags: Stringable[] ): Ratlogger => { - let originalTags = tags; + let originalTags = tags; - return enrichFunction( - ( - message: Stringable, - fields?: RatlogData["fields"], - ...tags: Stringable[] - ): void => { - getWriteFunc(writer)({ - message, - fields, - tags: originalTags.concat(tags), - }); - }, - { - tag: ( - /** a list of tags to apply to every output from this logger */ - ...tags: Stringable[] - ): Ratlogger => generateRatlogger(writer, ...originalTags.concat(tags)), - } - ); + return enrichFunction( + ( + message: Stringable, + fields?: RatlogData["fields"], + ...tags: Stringable[] + ): void => { + getWriteFunc(writer)({ + message, + fields, + tags: originalTags.concat(tags), + }); + }, + { + tag: ( + /** a list of tags to apply to every output from this logger */ + ...tags: Stringable[] + ): Ratlogger => generateRatlogger(writer, ...originalTags.concat(tags)), + }, + ); }; const ratlog = (() => { - return enrichFunction( - /** + return enrichFunction( + /** * @param writer a writable stream or function * @param tags a list of tags to apply to every output from this logger * @returns a logging function bound to the writer */ - (writer: Writer, ...tags: Stringable[]) => - generateRatlogger( - (data: RatlogData) => getWriteFunc(writer)(Ratlog.format(data)), - ...tags - ), - { - /** Constructor for more customizable logger instances. */ - logger: generateRatlogger, + (writer: Writer, ...tags: Stringable[]) => + generateRatlogger( + (data: RatlogData) => getWriteFunc(writer)(Ratlog.format(data)), + ...tags, + ), + { + /** Constructor for more customizable logger instances. */ + logger: generateRatlogger, - /** Exposure of the core Ratlog string formatter. */ - stringify: Ratlog.format, + /** Exposure of the core Ratlog string formatter. */ + stringify: Ratlog.format, - /** Exposure of the core Ratlog parser. */ - parse: Ratlog.parse, - } - ); + /** Exposure of the core Ratlog parser. */ + parse: Ratlog.parse, + }, + ); })(); export default ratlog; diff --git a/ratlog.ts b/ratlog.ts index 071c676..1d9bde5 100644 --- a/ratlog.ts +++ b/ratlog.ts @@ -1,12 +1,12 @@ import { - escape, - escapeField, - escapeMessage, - escapeTag, - unescape, - unescapeField, - unescapeMessage, - unescapeTag, + escape, + escapeField, + escapeMessage, + escapeTag, + unescape, + unescapeField, + unescapeMessage, + unescapeTag, } from "./stringmanip.ts"; /** @@ -20,78 +20,80 @@ export type Stringable = string | { toString: () => string }; * The base Ratlog data type. All logs are serialized from and parsed to objects of this type. */ export interface RatlogData { - message: Stringable; - tags?: Stringable[]; - fields?: Record; + message: Stringable; + tags?: Stringable[]; + fields?: Record; } export default class Ratlog { - /** + /** * Take a 'line' of Ratlog data and format it for output. * @param data the log line to format */ - static format(data: RatlogData): string { - let tagString = - data.tags?.length ?? 0 > 0 - ? `[${(data.tags ?? []) - .map((tag) => escapeTag(tag.toString())) - .join("|")}] ` - : ``; + static format(data: RatlogData): string { + let tagString = data.tags?.length ?? 0 > 0 + ? `[${ + (data.tags ?? []) + .map((tag) => escapeTag(tag.toString())) + .join("|") + }] ` + : ``; - let messageString = escapeMessage(data.message.toString() ?? ""); + let messageString = escapeMessage(data.message.toString() ?? ""); - let fieldString = Object.entries(data.fields ?? {}) - .map((entry) => - entry.map((subentry) => - subentry != null ? escapeField(subentry.toString()) : subentry - ) - ) - .map((entry) => `${entry[0]}${entry[1] != null ? `: ${entry[1]}` : ``}`) - .reduce((prev, cur) => `${prev} | ${cur}`, ""); + let fieldString = Object.entries(data.fields ?? {}) + .map((entry) => + entry.map((subentry) => + subentry != null ? escapeField(subentry.toString()) : subentry + ) + ) + .map((entry) => `${entry[0]}${entry[1] != null ? `: ${entry[1]}` : ``}`) + .reduce((prev, cur) => `${prev} | ${cur}`, ""); - return escape("\n")(tagString + messageString + fieldString) + "\n"; - } + return escape("\n")(tagString + messageString + fieldString) + "\n"; + } - /** + /** * Take a string and parse it. * * Known to work on standards-compliant Ratlog formatter output. Not guaranteed to work with log data that doesn't meet the spec. * * @param logline a line of text to parse as Ratlog data */ - static parse(logline: string): RatlogData { - let data: Partial = {}; + static parse(logline: string): RatlogData { + let data: Partial = {}; - logline = logline.replace(/\n$/, ""); // Trim off the newline at the end + logline = logline.replace(/\n$/, ""); // Trim off the newline at the end - logline = unescape("\n")(logline); + logline = unescape("\n")(logline); - let tagSection = logline.match(/^\[(.*(? 0) - data.fields = logline - .split(/ (? { - let parts = elem.split(/(? 0) { + data.fields = logline + .split(/ (? { + let parts = elem.split(/(? string)[]) => - mut.reduce((prev, func) => func(prev), str); + mut.reduce((prev, func) => func(prev), str); /** * Generate a function that escapes a given character for input strings * @param symbol a symbol (character) to generate an escape function for */ -export const escape = (symbol: string) => ( - /** A string to escape */ input: string -) => { - switch (symbol) { - case "\n": - return input.replaceAll("\n", `\\n`); - default: - return input.replaceAll(symbol, `\\${symbol}`); - } -}; +export const escape = (symbol: string) => + ( + /** A string to escape */ input: string, + ) => { + switch (symbol) { + case "\n": + return input.replaceAll("\n", `\\n`); + default: + return input.replaceAll(symbol, `\\${symbol}`); + } + }; /** * Generate a function that unescapes a given character for input strings * @param symbol a symbol (character) to generate an unescape function for */ -export const unescape = (symbol: string) => ( - /** A string to unescape */ input: string -) => { - switch (symbol) { - case "\n": - return input.replaceAll(`\\n`, `\n`); - default: - return input.replaceAll(`\\${symbol}`, symbol); - } -}; +export const unescape = (symbol: string) => + ( + /** A string to unescape */ input: string, + ) => { + switch (symbol) { + case "\n": + return input.replaceAll(`\\n`, `\n`); + default: + return input.replaceAll(`\\${symbol}`, symbol); + } + }; /** Escape symbols unsafe for tags */ export const escapeTag = (tag: string) => - apply(tag, [escape("]"), escape("|")]); + apply(tag, [escape("]"), escape("|")]); /** Escape symbols unsafe for messages */ export const escapeMessage = (message: string) => - apply(message, [escape("["), escape("|")]); + apply(message, [escape("["), escape("|")]); /** Escape symbols unsafe for fields */ export const escapeField = (fieldval: string) => - apply(fieldval, [escape(":"), escape("|")]); + apply(fieldval, [escape(":"), escape("|")]); /** Unescape symbols unsafe for tags */ export const unescapeTag = (tag: string) => - apply(tag, [unescape("]"), unescape("|")]); + apply(tag, [unescape("]"), unescape("|")]); /** Unescape symbols unsafe for messages */ export const unescapeMessage = (message: string) => - apply(message, [unescape("["), unescape("|")]); + apply(message, [unescape("["), unescape("|")]); /** Unescape symbols unsafe for fields */ export const unescapeField = (fieldpart: string) => - apply(fieldpart, [unescape(":"), unescape("|")]); + apply(fieldpart, [unescape(":"), unescape("|")]); diff --git a/test.ts b/test.ts index 7e9d77b..eb2bfaf 100644 --- a/test.ts +++ b/test.ts @@ -2,89 +2,87 @@ import { assertEquals } from "./deps.ts"; import Ratlog, { RatlogData } from "./ratlog.ts"; interface TestCase { - log: string; - data: RatlogData; + log: string; + data: RatlogData; } interface TestcaseFile { - meta: {}; - generic: TestCase[]; - parsing: TestCase[]; + meta: {}; + generic: TestCase[]; + parsing: TestCase[]; } console.log("Attempting to pull up-to-date test cases from spec..."); let testCases: TestcaseFile = await fetch( - "https://raw.githubusercontent.com/ratlog/ratlog-spec/master/ratlog.testsuite.json" + "https://raw.githubusercontent.com/ratlog/ratlog-spec/master/ratlog.testsuite.json", ) - .then( - (resp) => resp.text(), - () => Deno.readTextFile("./testcases.json") - ) - .catch(() => { - console.error( - "Unable to load test cases! (run with --allow-net, or if cached, --allow-read)" - ); - Deno.exit(1); - }) - .then(async (content) => { - await Deno.writeTextFile("./testcases.json", content).catch(() => - console.warn("Unable to cache test cases! (run with --allow-write)") - ); - return JSON.parse(content); - }); + .then( + (resp) => resp.text(), + () => Deno.readTextFile("./testcases.json"), + ) + .catch(() => { + console.error( + "Unable to load test cases! (run with --allow-net, or if cached, --allow-read)", + ); + Deno.exit(1); + }) + .then(async (content) => { + await Deno.writeTextFile("./testcases.json", content).catch(() => + console.warn("Unable to cache test cases! (run with --allow-write)") + ); + return JSON.parse(content); + }); testCases.generic.map(({ log, data }) => - Deno.test(`core format ${JSON.stringify(data)}`, () => - assertEquals(Ratlog.format(data), log) - ) + Deno.test(`core format ${JSON.stringify(data)}`, () => + assertEquals(Ratlog.format(data), log)) ); testCases.generic.map(({ log, data }) => - Deno.test(`core parse "${log.trim()}"`, () => - assertEquals(Ratlog.parse(log), data) - ) + Deno.test(`core parse "${log.trim()}"`, () => + assertEquals(Ratlog.parse(log), data)) ); testCases.generic.map(({ log, data }) => - Deno.test(`classic-api format default ${JSON.stringify(data)}`, async () => { - const ratlog = await (await import("./classic-api.ts")).default; + Deno.test(`classic-api format default ${JSON.stringify(data)}`, async () => { + const ratlog = await (await import("./classic-api.ts")).default; - const write = (line: string) => assertEquals(line, log); + const write = (line: string) => assertEquals(line, log); - let logger = ratlog(write); + let logger = ratlog(write); - logger(data.message, data.fields, ...(data.tags ?? [])); - }) + logger(data.message, data.fields, ...(data.tags ?? [])); + }) ); testCases.generic.map(({ log, data }) => - Deno.test( - `classic-api format default bound tag ${JSON.stringify(data)}`, - async () => { - const ratlog = await (await import("./classic-api.ts")).default; + Deno.test( + `classic-api format default bound tag ${JSON.stringify(data)}`, + async () => { + const ratlog = await (await import("./classic-api.ts")).default; - const write = (line: string) => assertEquals(line, log); + const write = (line: string) => assertEquals(line, log); - let logger = ratlog(write, ...(data.tags ?? [])); + let logger = ratlog(write, ...(data.tags ?? [])); - logger(data.message, data.fields); - } - ) + logger(data.message, data.fields); + }, + ) ); testCases.generic.map(({ log, data }) => - Deno.test( - `classic-api format secondary bound tag ${JSON.stringify(data)}`, - async () => { - const ratlog = await (await import("./classic-api.ts")).default; + Deno.test( + `classic-api format secondary bound tag ${JSON.stringify(data)}`, + async () => { + const ratlog = await (await import("./classic-api.ts")).default; - const write = (line: string) => assertEquals(line, log); + const write = (line: string) => assertEquals(line, log); - let logger1 = ratlog(write); + let logger1 = ratlog(write); - let logger2 = logger1.tag(...(data.tags ?? [])); + let logger2 = logger1.tag(...(data.tags ?? [])); - logger2(data.message, data.fields); - } - ) + logger2(data.message, data.fields); + }, + ) );