aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/tools/index.ts3
-rw-r--r--src/tools/mac-address-generator/index.ts12
-rw-r--r--src/tools/mac-address-generator/mac-address-generator.e2e.spec.ts11
-rw-r--r--src/tools/mac-address-generator/mac-address-generator.vue103
-rw-r--r--src/tools/mac-address-generator/mac-adress-generator.models.test.ts43
-rw-r--r--src/tools/mac-address-generator/mac-adress-generator.models.ts18
-rw-r--r--src/utils/macAddress.ts16
7 files changed, 204 insertions, 2 deletions
diff --git a/src/tools/index.ts b/src/tools/index.ts
index e6df8cc..22db077 100644
--- a/src/tools/index.ts
+++ b/src/tools/index.ts
@@ -1,6 +1,7 @@
import { tool as base64FileConverter } from './base64-file-converter';
import { tool as base64StringConverter } from './base64-string-converter';
import { tool as basicAuthGenerator } from './basic-auth-generator';
+import { tool as macAddressGenerator } from './mac-address-generator';
import { tool as textToBinary } from './text-to-binary';
import { tool as ulidGenerator } from './ulid-generator';
import { tool as ibanValidatorAndParser } from './iban-validator-and-parser';
@@ -140,7 +141,7 @@ export const toolsByCategory: ToolCategory[] = [
},
{
name: 'Network',
- components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, ipv6UlaGenerator],
+ components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator],
},
{
name: 'Math',
diff --git a/src/tools/mac-address-generator/index.ts b/src/tools/mac-address-generator/index.ts
new file mode 100644
index 0000000..9d20fb6
--- /dev/null
+++ b/src/tools/mac-address-generator/index.ts
@@ -0,0 +1,12 @@
+import { Devices } from '@vicons/tabler';
+import { defineTool } from '../tool';
+
+export const tool = defineTool({
+ name: 'MAC address generator',
+ path: '/mac-address-generator',
+ description: 'Enter the quantity and prefix. MAC addresses will be generated in your chosen case (uppercase or lowercase)',
+ keywords: ['mac', 'address', 'generator', 'random', 'prefix'],
+ component: () => import('./mac-address-generator.vue'),
+ icon: Devices,
+ createdAt: new Date('2023-11-31'),
+});
diff --git a/src/tools/mac-address-generator/mac-address-generator.e2e.spec.ts b/src/tools/mac-address-generator/mac-address-generator.e2e.spec.ts
new file mode 100644
index 0000000..1d99ccd
--- /dev/null
+++ b/src/tools/mac-address-generator/mac-address-generator.e2e.spec.ts
@@ -0,0 +1,11 @@
+import { expect, test } from '@playwright/test';
+
+test.describe('Tool - MAC address generator', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/mac-address-generator');
+ });
+
+ test('Has correct title', async ({ page }) => {
+ await expect(page).toHaveTitle('MAC address generator - IT Tools');
+ });
+});
diff --git a/src/tools/mac-address-generator/mac-address-generator.vue b/src/tools/mac-address-generator/mac-address-generator.vue
new file mode 100644
index 0000000..725070d
--- /dev/null
+++ b/src/tools/mac-address-generator/mac-address-generator.vue
@@ -0,0 +1,103 @@
+<script setup lang="ts">
+import _ from 'lodash';
+import { generateRandomMacAddress } from './mac-adress-generator.models';
+import { computedRefreshable } from '@/composable/computedRefreshable';
+import { useCopy } from '@/composable/copy';
+import { usePartialMacAddressValidation } from '@/utils/macAddress';
+
+const amount = useStorage('mac-address-generator-amount', 1);
+const macAddressPrefix = useStorage('mac-address-generator-prefix', '64:16:7F');
+
+const prefixValidation = usePartialMacAddressValidation(macAddressPrefix);
+
+const casesTransformers = [
+ { label: 'Uppercase', value: (value: string) => value.toUpperCase() },
+ { label: 'Lowercase', value: (value: string) => value.toLowerCase() },
+];
+const caseTransformer = ref(casesTransformers[0].value);
+
+const separators = [
+ {
+ label: ':',
+ value: ':',
+ },
+ {
+ label: '-',
+ value: '-',
+ },
+ {
+ label: '.',
+ value: '.',
+ },
+ {
+ label: 'None',
+ value: '',
+ },
+];
+const separator = useStorage('mac-address-generator-separator', separators[0].value);
+
+const [macAddresses, refreshMacAddresses] = computedRefreshable(() => {
+ if (!prefixValidation.isValid) {
+ return '';
+ }
+
+ const ids = _.times(amount.value, () => caseTransformer.value(generateRandomMacAddress({
+ prefix: macAddressPrefix.value,
+ separator: separator.value,
+ })));
+ return ids.join('\n');
+});
+
+const { copy } = useCopy({ source: macAddresses, text: 'MAC addresses copied to the clipboard' });
+</script>
+
+<template>
+ <div flex flex-col justify-center gap-2>
+ <div flex items-center>
+ <label w-150px pr-12px text-right> Quantity:</label>
+ <n-input-number v-model:value="amount" min="1" max="100" flex-1 />
+ </div>
+
+ <c-input-text
+ v-model:value="macAddressPrefix"
+ label="MAC address prefix:"
+ placeholder="Set a prefix, e.g. 64:16:7F"
+ clearable
+ label-position="left"
+ spellcheck="false"
+ :validation="prefixValidation"
+ raw-text
+ label-width="150px"
+ label-align="right"
+ />
+
+ <c-buttons-select
+ v-model:value="caseTransformer"
+ :options="casesTransformers"
+ label="Case:"
+ label-width="150px"
+ label-align="right"
+ />
+
+ <c-buttons-select
+ v-model:value="separator"
+ :options="separators"
+ label="Separator:"
+ label-width="150px"
+ label-align="right"
+ />
+
+ <c-card mt-5 flex data-test-id="ulids">
+ <pre m-0 m-x-auto>{{ macAddresses }}</pre>
+ </c-card>
+
+ <div flex justify-center gap-2>
+ <c-button data-test-id="refresh" @click="refreshMacAddresses()">
+ Refresh
+ </c-button>
+ <c-button @click="copy()">
+ Copy
+ </c-button>
+ </div>
+ </div>
+</template>
diff --git a/src/tools/mac-address-generator/mac-adress-generator.models.test.ts b/src/tools/mac-address-generator/mac-adress-generator.models.test.ts
new file mode 100644
index 0000000..5b66025
--- /dev/null
+++ b/src/tools/mac-address-generator/mac-adress-generator.models.test.ts
@@ -0,0 +1,43 @@
+import { describe, expect, it } from 'vitest';
+import { generateRandomMacAddress, splitPrefix } from './mac-adress-generator.models';
+
+describe('mac-adress-generator models', () => {
+ describe('splitPrefix', () => {
+ it('a mac address prefix is splitted around non hex characters', () => {
+ expect(splitPrefix('')).toEqual([]);
+ expect(splitPrefix('01')).toEqual(['01']);
+ expect(splitPrefix('01:')).toEqual(['01']);
+ expect(splitPrefix('01:23')).toEqual(['01', '23']);
+ expect(splitPrefix('01-23')).toEqual(['01', '23']);
+ });
+
+ it('when a prefix contains only hex characters, they are grouped by 2', () => {
+ expect(splitPrefix('0123')).toEqual(['01', '23']);
+ expect(splitPrefix('012345')).toEqual(['01', '23', '45']);
+ expect(splitPrefix('0123456')).toEqual(['01', '23', '45', '06']);
+ });
+ });
+
+ describe('generateRandomMacAddress', () => {
+ const createRandomByteGenerator = () => {
+ let i = 0;
+ return () => (i++).toString(16).padStart(2, '0');
+ };
+
+ it('generates a random mac address', () => {
+ expect(generateRandomMacAddress({ getRandomByte: createRandomByteGenerator() })).toBe('00:01:02:03:04:05');
+ });
+
+ it('generates a random mac address with a prefix', () => {
+ expect(generateRandomMacAddress({ prefix: 'ff:ee:aa', getRandomByte: createRandomByteGenerator() })).toBe('ff:ee:aa:00:01:02');
+ expect(generateRandomMacAddress({ prefix: 'ff:ee:a', getRandomByte: createRandomByteGenerator() })).toBe('ff:ee:0a:00:01:02');
+ });
+
+ it('generates a random mac address with a prefix and a different separator', () => {
+ expect(generateRandomMacAddress({ prefix: 'ff-ee-aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02');
+ expect(generateRandomMacAddress({ prefix: 'ff:ee:aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02');
+ expect(generateRandomMacAddress({ prefix: 'ff-ee:aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02');
+ expect(generateRandomMacAddress({ prefix: 'ff ee:aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02');
+ });
+ });
+});
diff --git a/src/tools/mac-address-generator/mac-adress-generator.models.ts b/src/tools/mac-address-generator/mac-adress-generator.models.ts
new file mode 100644
index 0000000..45e434e
--- /dev/null
+++ b/src/tools/mac-address-generator/mac-adress-generator.models.ts
@@ -0,0 +1,18 @@
+import _ from 'lodash';
+
+export { splitPrefix, generateRandomMacAddress };
+
+function splitPrefix(prefix: string): string[] {
+ const base = prefix.match(/[^0-9a-f]/i) === null ? prefix.match(/.{1,2}/g) ?? [] : prefix.split(/[^0-9a-f]/i);
+
+ return base.filter(Boolean).map(byte => byte.padStart(2, '0'));
+}
+
+function generateRandomMacAddress({ prefix: rawPrefix = '', separator = ':', getRandomByte = () => _.random(0, 255).toString(16).padStart(2, '0') }: { prefix?: string; separator?: string; getRandomByte?: () => string } = {}) {
+ const prefix = splitPrefix(rawPrefix);
+
+ const randomBytes = _.times(6 - prefix.length, getRandomByte);
+ const bytes = [...prefix, ...randomBytes];
+
+ return bytes.join(separator);
+}
diff --git a/src/utils/macAddress.ts b/src/utils/macAddress.ts
index 4488b32..f55c334 100644
--- a/src/utils/macAddress.ts
+++ b/src/utils/macAddress.ts
@@ -15,4 +15,18 @@ function macAddressValidation(value: Ref) {
});
}
-export { macAddressValidation, macAddressValidationRules };
+const partialMacAddressValidationRules = [
+ {
+ message: 'Invalid partial MAC address',
+ validator: (value: string) => value.trim().match(/^([0-9a-f]{2}[:\-. ]){0,5}([0-9a-f]{0,2})$/i),
+ },
+];
+
+function usePartialMacAddressValidation(value: Ref) {
+ return useValidation({
+ source: value,
+ rules: partialMacAddressValidationRules,
+ });
+}
+
+export { macAddressValidation, macAddressValidationRules, usePartialMacAddressValidation, partialMacAddressValidationRules };