Skip to content

Commit

Permalink
feat: rewrite datatype to ts
Browse files Browse the repository at this point in the history
  • Loading branch information
Shinigami92 authored and damienwebdev committed Jan 14, 2022
1 parent b20f80b commit e0d0b5c
Show file tree
Hide file tree
Showing 4 changed files with 349 additions and 0 deletions.
253 changes: 253 additions & 0 deletions src/datatype.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import type { Faker } from '.';

export class Datatype {
constructor(private readonly faker: Faker, seed?: any[] | any) {
// Use a user provided seed if it is an array or number
if (Array.isArray(seed) && seed.length) {
this.faker.mersenne.seed_array(seed);
} else if (!isNaN(seed)) {
this.faker.mersenne.seed(seed);
}
}

/**
* Returns a single random number based on a max number or range.
*
* @method faker.datatype.number
* @param options
*/
number(
options?: number | { min?: number; max?: number; precision?: number }
): number {
if (typeof options === 'number') {
options = { max: options };
}

options = options ?? {};

if (typeof options.min === 'undefined') {
options.min = 0;
}

if (typeof options.max === 'undefined') {
options.max = 99999;
}

if (typeof options.precision === 'undefined') {
options.precision = 1;
}

// Make the range inclusive of the max value
let max = options.max;
if (max >= 0) {
max += options.precision;
}

let randomNumber = Math.floor(
this.faker.mersenne.rand(
max / options.precision,
options.min / options.precision
)
);
// Workaround problem in Float point arithmetics for e.g. 6681493 / 0.01
randomNumber = randomNumber / (1 / options.precision);

return randomNumber;
}

/**
* Returns a single random floating-point number based on a max number or range.
*
* @method faker.datatype.float
* @param options
*/
float(
options?: number | { min?: number; max?: number; precision?: number }
): number {
if (typeof options === 'number') {
options = {
precision: options,
};
}
options = options || {};
const opts: { precision?: number } = {};
for (const p in options) {
opts[p] = options[p];
}
if (typeof opts.precision === 'undefined') {
opts.precision = 0.01;
}
return this.faker.datatype.number(opts);
}

/**
* Returns a Date object using a random number of milliseconds since 1. Jan 1970 UTC
* Caveat: seeding is not working
*
* @method faker.datatype.datetime
* @param options pass min OR max as number of milliseconds since 1. Jan 1970 UTC
*/
datetime(options): Date {
if (typeof options === 'number') {
options = {
max: options,
};
}

const minMax = 8640000000000000;

options = options || {};

if (typeof options.min === 'undefined' || options.min < minMax * -1) {
options.min = new Date().setFullYear(1990, 1, 1);
}

if (typeof options.max === 'undefined' || options.max > minMax) {
options.max = new Date().setFullYear(2100, 1, 1);
}

const random = this.faker.datatype.number(options);
return new Date(random);
}

/**
* Returns a string, containing UTF-16 chars between 33 and 125 ('!' to '}')
*
*
* @method faker.datatype.string
* @param length length of generated string, default = 10, max length = 2^20
*/
string(length: number = 10): string {
const maxLength = Math.pow(2, 20);
if (length >= maxLength) {
length = maxLength;
}

const charCodeOption = {
min: 33,
max: 125,
};

let returnString = '';

for (var i = 0; i < length; i++) {
returnString += String.fromCharCode(
this.faker.datatype.number(charCodeOption)
);
}

return returnString;
}

/**
* uuid
*
* @method faker.datatype.uuid
*/
uuid(): string {
const RFC4122_TEMPLATE = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
const replacePlaceholders = (placeholder) => {
const random = this.faker.datatype.number({ min: 0, max: 15 });
const value = placeholder == 'x' ? random : (random & 0x3) | 0x8;
return value.toString(16);
};
return RFC4122_TEMPLATE.replace(/[xy]/g, replacePlaceholders);
}

/**
* boolean
*
* @method faker.datatype.boolean
*/
boolean(): boolean {
return !!this.faker.datatype.number(1);
}

/**
* hexaDecimal
*
* @method faker.datatype.hexaDecimal
* @param {number} count defaults to 1
*/
hexaDecimal(count: number = 1): string {
let wholeString = '';
for (let i = 0; i < count; i++) {
wholeString += this.faker.random.arrayElement([
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'a',
'b',
'c',
'd',
'e',
'f',
'A',
'B',
'C',
'D',
'E',
'F',
]);
}

return '0x' + wholeString;
}

/**
* Returns json object with 7 pre-defined properties
*
* @method faker.datatype.json
*/
json(): string {
const properties = ['foo', 'bar', 'bike', 'a', 'b', 'name', 'prop'];

const returnObject: Record<string, string | number> = {};
properties.forEach((prop) => {
returnObject[prop] = this.faker.datatype.boolean()
? this.faker.datatype.string()
: this.faker.datatype.number();
});

return JSON.stringify(returnObject);
}

/**
* Returns an array with values generated by faker.datatype.number and faker.datatype.string
*
* @method faker.datatype.array
* @param length length of the returned array
*/

array(length: number = 10): Array<string | number> {
const returnArray = new Array(length);
for (var i = 0; i < length; i++) {
returnArray[i] = this.faker.datatype.boolean()
? this.faker.datatype.string()
: this.faker.datatype.number();
}
return returnArray;
}

/**
* Returns a Big Integer with values generated by faker.datatype.bigInt
*
* @method faker.datatype.bigInt
* @param value
*/
bigInt(value?: string | number | bigint | boolean): bigint {
if (value === undefined) {
value =
Math.floor(this.faker.datatype.number() * 99999999999) + 10000000000;
}

return BigInt(value);
}
}
36 changes: 36 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Datatype } from './datatype';
import { Mersenne } from './mersenne';
import { Random } from './random';

export interface FakerOptions {
locales?: string[];
locale?: string;
localeFallback?: string;
}

export class Faker {
locales: string[] | {};
locale: string;
localeFallback: string;

seedValue?: any[] | any;

readonly mersenne: Mersenne = new Mersenne();
random = new Random(this);
datatype: Datatype = new Datatype(this);

constructor(opts: FakerOptions = {}) {
this.locales = this.locales || opts.locales || {};
this.locale = this.locale || opts.locale || 'en';
this.localeFallback = this.localeFallback || opts.localeFallback || 'en';
}

seed(value?: any[] | any) {
this.seedValue = value;
this.random = new Random(this, this.seedValue);
this.datatype = new Datatype(this, this.seedValue);
}
}

export default Faker;
module.exports = Faker;
36 changes: 36 additions & 0 deletions src/mersenne.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const Gen = require('../vendor/mersenne').MersenneTwister19937;

export class Mersenne {
private gen = new Gen();

constructor() {
this.gen.init_genrand(new Date().getTime() % 1000000000);
}

rand(max, min) {
if (max === undefined) {
min = 0;
max = 32768;
}

return Math.floor(this.gen.genrand_real2() * (max - min) + min);
}

seed(S) {
if (typeof S != 'number') {
throw new Error('seed(S) must take numeric argument; is ' + typeof S);
}

this.gen.init_genrand(S);
}

seed_array(A) {
if (typeof A != 'object') {
throw new Error(
'seed_array(A) must take array of numbers; is ' + typeof A
);
}

this.gen.init_by_array(A, A.length);
}
}
24 changes: 24 additions & 0 deletions src/random.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Faker } from '.';

export class Random {
constructor(private readonly faker: Faker, seed?: any[] | any) {
// Use a user provided seed if it is an array or number
if (Array.isArray(seed) && seed.length) {
this.faker.mersenne.seed_array(seed);
} else if (!isNaN(seed)) {
this.faker.mersenne.seed(seed);
}
}

/**
* Takes an array and returns a random element of the array.
*
* @method faker.random.arrayElement
* @param array
*/
arrayElement(array) {
array = array || ['a', 'b', 'c'];
var r = this.faker.datatype.number({ max: array.length - 1 });
return array[r];
}
}

0 comments on commit e0d0b5c

Please sign in to comment.