diff --git a/src/formats/index.ts b/src/formats/index.ts index bc14475..96c9480 100644 --- a/src/formats/index.ts +++ b/src/formats/index.ts @@ -1,3 +1,4 @@ +import { inferMAC, JSONMACFormat } from './mac'; // This is the order the formats will be run in import { inferDatetime, JSONDateTimeFormat } from "./datetime"; import { inferTimestamp, JSONTimestampFormat } from "./timestamp"; @@ -63,7 +64,8 @@ export type JSONStringFormat = | JSONSemverFormat | JSONJWTStringFormat | JSONColorFormat - | JSONCreditCardFormat; + | JSONCreditCardFormat + | JSONMACFormat; const formats = [ inferUri, @@ -86,6 +88,7 @@ const formats = [ inferJWT, inferColor, inferCreditCard, + inferMAC, ]; export function inferFormat(value: string): JSONStringFormat | undefined { diff --git a/src/formats/mac.ts b/src/formats/mac.ts new file mode 100644 index 0000000..151e885 --- /dev/null +++ b/src/formats/mac.ts @@ -0,0 +1,27 @@ +export type JSONMACFormat = { + name: "mac"; + variant: "EUI-48" | "EUI-64"; + splitter: ":" | "." | "-"; +}; + +const macRegexDDot = /^[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}$/; +const macRegexDot = /^[a-fA-F0-9]{2}(\.[a-fA-F0-9]{2}){5}$/; +const macRegexDash = /^[a-fA-F0-9]{2}(-[a-fA-F0-9]{2}){5}$/; + +const mac64RegexDDot = /^[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){7}$/; +const mac64RegexDot = /^[a-fA-F0-9]{2}(.[a-fA-F0-9]{2}){7}$/; +const mac64RegexDash = /^[a-fA-F0-9]{2}(-[a-fA-F0-9]{2}){7}$/; + +export function inferMAC(value: string): JSONMACFormat | undefined { + if (value.length === 17) { + if (macRegexDDot.exec(value)) return { name: "mac", variant: "EUI-48", splitter: ":" }; + else if (macRegexDot.exec(value)) return { name: "mac", variant: "EUI-48", splitter: "." }; + else if (macRegexDash.exec(value)) return { name: "mac", variant: "EUI-48", splitter: "-" }; + } else if (value.length === 23) { + if (mac64RegexDDot.exec(value)) return { name: "mac", variant: "EUI-64", splitter: ":" }; + else if (mac64RegexDot.exec(value)) return { name: "mac", variant: "EUI-64", splitter: "." }; + else if (mac64RegexDash.exec(value)) return { name: "mac", variant: "EUI-64", splitter: "-" }; + } + + return undefined; +} diff --git a/tests/stringFormats.test.ts b/tests/stringFormats.test.ts index cf339ae..c00e5c0 100644 --- a/tests/stringFormats.test.ts +++ b/tests/stringFormats.test.ts @@ -824,6 +824,54 @@ describe("credit cards", () => { ); }); +describe("mac", () => { + test.each([ + "38:EE:4A:47:3A:36", + "ad:af:bd:71:12:87", + ])("%p should be infered as being a MAC address", (value) => { + expect(inferType(value)).toEqual({ + name: "string", + value, + format: { + name: "mac", + variant: "EUI-48", + splitter: ":" + }, + }); + }); + + test.each([ + "38-EE-4A-47-3A-36", + "ad-af-bd-71-12-87", + ])("%p should be infered as being a MAC address", (value) => { + expect(inferType(value)).toEqual({ + name: "string", + value, + format: { + name: "mac", + variant: "EUI-48", + splitter: "-" + }, + }); + }); + + test.each([ + "38.EE.4A.47.3A.36", + "ad.af.bd.71.12.87", + ])("%p should be infered as being a MAC address", (value) => { + expect(inferType(value)).toEqual({ + name: "string", + value, + format: { + name: "mac", + variant: "EUI-48", + splitter: "." + }, + }); + }); +}); + + describe("without format", () => { test.each(["46", "2244994945", "1212092628029698048"])( "%p should be inferred as having no format",