Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mac address validation #270

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to the library will be documented in this file.

## vX.X.X (Month DD, YYYY)

- Add `mac`, `mac48` and `mac64` validation function (pull request #270)

## v0.22.0 (December 03, 2023)

- Add support for boolean to `notValue` validation (pull request #261)
Expand Down
12 changes: 12 additions & 0 deletions library/src/regex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ export const ISO_TIMESTAMP_REGEX =
*/
export const ISO_WEEK_REGEX = /^\d{4}-W(?:0[1-9]|[1-4]\d|5[0-3])$/u;

/**
* [MAC](https://en.wikipedia.org/wiki/MAC_address) 48 bit regex.
*/
export const MAC48_REGEX =
/^(?:[\da-f]{2}:){5}[\da-f]{2}$|^(?:[\da-f]{2}-){5}[\da-f]{2}$|^(?:[\da-f]{4}\.){2}[\da-f]{4}$/iu;

/**
* [MAC](https://en.wikipedia.org/wiki/MAC_address) 64 bit regex.
*/
export const MAC64_REGEX =
/^(?:[\da-f]{2}:){7}[\da-f]{2}$|^(?:[\da-f]{2}-){7}[\da-f]{2}$|^(?:[\da-f]{4}\.){3}[\da-f]{4}$|^(?:[\da-f]{4}:){3}[\da-f]{4}$/iu;

/**
* [ULID](https://github.com/ulid/spec) regex.
*/
Expand Down
3 changes: 3 additions & 0 deletions library/src/validations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export * from './isoTimeSecond/index.ts';
export * from './isoTimestamp/index.ts';
export * from './isoWeek/index.ts';
export * from './length/index.ts';
export * from './mac/index.ts';
export * from './mac48/index.ts';
export * from './mac64/index.ts';
export * from './maxBytes/index.ts';
export * from './maxLength/index.ts';
export * from './maxSize/index.ts';
Expand Down
1 change: 1 addition & 0 deletions library/src/validations/mac/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './mac.ts';
45 changes: 45 additions & 0 deletions library/src/validations/mac/mac.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { describe, expect, test } from 'vitest';
import { mac } from './mac.ts';

describe('mac', () => {
test('should pass only MAC address', () => {
const validate = mac();

const value1 = 'b6:05:20:67:f9:58';
expect(validate._parse(value1).output).toBe(value1);
const value2 = 'b6-05-20-67-f9-58';
expect(validate._parse(value2).output).toBe(value2);
const value3 = 'b605.2067.f958';
expect(validate._parse(value3).output).toBe(value3);
const value4 = '00:25:96:FF:FE:12:34:56';
expect(validate._parse(value4).output).toBe(value4);
const value5 = '00-1A-2B-3C-4D-5E-6F-70';
expect(validate._parse(value5).output).toBe(value5);
const value6 = '0025.96FF.FE12.3456';
expect(validate._parse(value6).output).toBe(value6);
const value7 = '0025:96FF:FE12:3456';
expect(validate._parse(value7).output).toBe(value7);

expect(validate._parse('').issues).toBeTruthy();
expect(validate._parse('00:1G:2B:3C:4D:5E').issues).toBeTruthy();
expect(validate._parse('00:1A:2B:3C:4D').issues).toBeTruthy();
expect(validate._parse('00:1A:2B:3C:4D:5E:6F').issues).toBeTruthy();
expect(validate._parse('00:1A:2B:3C:4D:5E:6F:70:80').issues).toBeTruthy();
expect(validate._parse('b605-2067-f958').issues).toBeTruthy();
expect(validate._parse('00_1A_2B_3C_4D_5E').issues).toBeTruthy();
expect(validate._parse('001A2B3C4D5E6F').issues).toBeTruthy();
expect(validate._parse('ZZ:ZZ:ZZ:ZZ:ZZ:ZZ').issues).toBeTruthy();
expect(
validate._parse('00:1A:2B:3C:4D:5E:6F:70:80:90:AB').issues
).toBeTruthy();
expect(validate._parse('001122334455').issues).toBeTruthy();
expect(validate._parse('00:1A:2B:3C:4D:5E:6F:70:ZZ').issues).toBeTruthy();
expect(validate._parse('GHIJ:KLNM:OPQR').issues).toBeTruthy();
});

test('should return custom error message', () => {
const error = 'Value is not MAC address!';
const validate = mac(error);
expect(validate._parse('test').issues?.[0].message).toBe(error);
});
});
41 changes: 41 additions & 0 deletions library/src/validations/mac/mac.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { MAC48_REGEX, MAC64_REGEX } from '../../regex.ts';
import type { BaseValidation, ErrorMessage } from '../../types/index.ts';
import { actionIssue, actionOutput } from '../../utils/index.ts';

/**
* MAC validation type.
*/
export type MacValidation<TInput extends string> = BaseValidation<TInput> & {
/**
* The validation type.
*/
type: 'mac';
/**
* The MAC 48 and 64 bit regex.
*/
requirement: [RegExp, RegExp];
};

/**
* Creates a validation function that validates a [MAC](https://en.wikipedia.org/wiki/MAC_address).
*
* @param message The error message.
*
* @returns A validation function.
*/
export function mac<TInput extends string>(
message: ErrorMessage = 'Invalid MAC'
): MacValidation<TInput> {
return {
type: 'mac',
async: false,
message,
requirement: [MAC48_REGEX, MAC64_REGEX],
_parse(input) {
return !this.requirement[0].test(input) &&
!this.requirement[1].test(input)
? actionIssue(this.type, this.message, input, this.requirement)
: actionOutput(input);
},
};
}
1 change: 1 addition & 0 deletions library/src/validations/mac48/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './mac48.ts';
34 changes: 34 additions & 0 deletions library/src/validations/mac48/mac48.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, expect, test } from 'vitest';
import { mac48 } from './mac48.ts';

describe('mac48', () => {
test('should pass only MAC 48 address', () => {
const validate = mac48();

const value1 = 'b6:05:20:67:f9:58';
expect(validate._parse(value1).output).toBe(value1);
const value2 = 'b6-05-20-67-f9-58';
expect(validate._parse(value2).output).toBe(value2);
const value3 = 'b605.2067.f958';
expect(validate._parse(value3).output).toBe(value3);

expect(validate._parse('').issues).toBeTruthy();
expect(validate._parse('00:1G:2B:3C:4D:5E').issues).toBeTruthy();
expect(validate._parse('00:1A:2B:3C:4D').issues).toBeTruthy();
expect(validate._parse('b605-2067-f958').issues).toBeTruthy();
expect(validate._parse('00_1A_2B_3C_4D_5E').issues).toBeTruthy();
expect(validate._parse('001A2B3C4D5E6F').issues).toBeTruthy();
expect(validate._parse('ZZ:ZZ:ZZ:ZZ:ZZ:ZZ').issues).toBeTruthy();
expect(
validate._parse('00:1A:2B:3C:4D:5E:6F:70:80:90:AB').issues
).toBeTruthy();
expect(validate._parse('001122334455').issues).toBeTruthy();
expect(validate._parse('GHIJ:KLNM:OPQR').issues).toBeTruthy();
});

test('should return custom error message', () => {
const error = 'Value is not MAC address!';
const validate = mac48(error);
expect(validate._parse('test').issues?.[0].message).toBe(error);
});
});
40 changes: 40 additions & 0 deletions library/src/validations/mac48/mac48.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { MAC48_REGEX } from '../../regex.ts';
import type { BaseValidation, ErrorMessage } from '../../types/index.ts';
import { actionIssue, actionOutput } from '../../utils/index.ts';

/**
* MAC validation type.
*/
export type Mac48Validation<TInput extends string> = BaseValidation<TInput> & {
/**
* The validation type.
*/
type: 'mac48';
/**
* The 48 bit MAC regex.
*/
requirement: RegExp;
};

/**
* Creates a validation function that validates a 48 bit [MAC](https://en.wikipedia.org/wiki/MAC_address).
*
* @param message The error message.
*
* @returns A validation function.
*/
export function mac48<TInput extends string>(
message: ErrorMessage = 'Invalid 48 bit MAC'
): Mac48Validation<TInput> {
return {
type: 'mac48',
async: false,
message,
requirement: MAC48_REGEX,
_parse(input) {
return !this.requirement.test(input)
? actionIssue(this.type, this.message, input, this.requirement)
: actionOutput(input);
},
};
}
1 change: 1 addition & 0 deletions library/src/validations/mac64/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './mac64.ts';
39 changes: 39 additions & 0 deletions library/src/validations/mac64/mac64.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, expect, test } from 'vitest';
import { mac64 } from './mac64.ts';

describe('mac64', () => {
test('should pass only MAC address', () => {
const validate = mac64();

const value1 = '00:25:96:FF:FE:12:34:56';
expect(validate._parse(value1).output).toBe(value1);
const value2 = '00-1A-2B-3C-4D-5E-6F-70';
expect(validate._parse(value2).output).toBe(value2);
const value3 = '0025.96FF.FE12.3456';
expect(validate._parse(value3).output).toBe(value3);
const value4 = '0025:96FF:FE12:3456';
expect(validate._parse(value4).output).toBe(value4);

expect(validate._parse('').issues).toBeTruthy();
expect(validate._parse('00:1G:2B:3C:4D:5E').issues).toBeTruthy();
expect(validate._parse('00:1A:2B:3C:4D:5E:6F').issues).toBeTruthy();
expect(validate._parse('0025.96FF.FE12').issues).toBeTruthy();
expect(validate._parse('00:1A:2B:3C:4D:5E:6F:70:80').issues).toBeTruthy();
expect(validate._parse('b605-2067-f958').issues).toBeTruthy();
expect(validate._parse('00_1A_2B_3C_4D_5E').issues).toBeTruthy();
expect(validate._parse('001A2B3C4D5E6F').issues).toBeTruthy();
expect(validate._parse('ZZ:ZZ:ZZ:ZZ:ZZ:ZZ').issues).toBeTruthy();
expect(
validate._parse('00:1A:2B:3C:4D:5E:6F:70:80:90:AB').issues
).toBeTruthy();
expect(validate._parse('001122334455').issues).toBeTruthy();
expect(validate._parse('00:1A:2B:3C:4D:5E:6F:70:ZZ').issues).toBeTruthy();
expect(validate._parse('GHIJ:KLNM:OPQR').issues).toBeTruthy();
});

test('should return custom error message', () => {
const error = 'Value is not MAC address!';
const validate = mac64(error);
expect(validate._parse('test').issues?.[0].message).toBe(error);
});
});
40 changes: 40 additions & 0 deletions library/src/validations/mac64/mac64.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { MAC64_REGEX } from '../../regex.ts';
import type { BaseValidation, ErrorMessage } from '../../types/index.ts';
import { actionIssue, actionOutput } from '../../utils/index.ts';

/**
* MAC validation type.
*/
export type Mac64Validation<TInput extends string> = BaseValidation<TInput> & {
/**
* The validation type.
*/
type: 'mac64';
/**
* The 64 bit MAC regex.
*/
requirement: RegExp;
};

/**
* Creates a validation function that validates a 64 bit [MAC](https://en.wikipedia.org/wiki/MAC_address).
*
* @param message The error message.
*
* @returns A validation function.
*/
export function mac64<TInput extends string>(
message: ErrorMessage = 'Invalid 64 bit MAC'
): Mac64Validation<TInput> {
return {
type: 'mac64',
async: false,
message,
requirement: MAC64_REGEX,
_parse(input) {
return !this.requirement.test(input)
? actionIssue(this.type, this.message, input, this.requirement)
: actionOutput(input);
},
};
}
2 changes: 1 addition & 1 deletion library/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"skipLibCheck": true,
"noEmit": true
},
"include": ["src", "./*.ts"]
"include": ["src", "./*.config.ts"]
}
3 changes: 3 additions & 0 deletions website/src/routes/api/(schemas)/string/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ The following APIs can be combined with `string`.
'isoTimestamp',
'isoWeek',
'length',
'mac',
'mac48',
'mac64',
'maxBytes',
'maxLength',
'maxValue',
Expand Down
7 changes: 7 additions & 0 deletions website/src/routes/api/(validations)/mac/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: mac
---

# mac

> The content of this page is not yet ready. Until then just use the [source code](https://github.com/fabian-hiller/valibot/blob/main/library/src/validations/mac/mac.ts).
7 changes: 7 additions & 0 deletions website/src/routes/api/(validations)/mac48/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: mac48
---

# mac48

> The content of this page is not yet ready. Until then just use the [source code](https://github.com/fabian-hiller/valibot/blob/main/library/src/validations/mac48/mac48.ts).
7 changes: 7 additions & 0 deletions website/src/routes/api/(validations)/mac64/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: mac64
---

# mac64

> The content of this page is not yet ready. Until then just use the [source code](https://github.com/fabian-hiller/valibot/blob/main/library/src/validations/mac64/mac64.ts).
3 changes: 3 additions & 0 deletions website/src/routes/api/menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
- [isoTimestamp](/api/isoTimestamp)
- [isoWeek](/api/isoWeek)
- [length](/api/length)
- [mac](/api/mac)
- [mac48](/api/mac48)
- [mac64](/api/mac64)
- [maxBytes](/api/maxBytes)
- [maxLength](/api/maxLength)
- [maxSize](/api/maxSize)
Expand Down
3 changes: 3 additions & 0 deletions website/src/routes/guides/(main-concepts)/pipelines/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ Validation functions examine the input and, if the input does not meet a certain
'isoTimestamp',
'isoWeek',
'length',
'mac',
'mac48',
'mac64',
'maxBytes',
'maxLength',
'maxSize',
Expand Down
Loading