Skip to content

Commit

Permalink
feat(NODE-6086): add Double.fromString() method (#671)
Browse files Browse the repository at this point in the history
  • Loading branch information
aditi-khare-mongoDB authored Apr 18, 2024
1 parent 5a21889 commit e943cdb
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 0 deletions.
33 changes: 33 additions & 0 deletions src/double.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BSONValue } from './bson_value';
import { BSONError } from './error';
import type { EJSONOptions } from './extended_json';
import { type InspectFn, defaultInspect } from './parser/utils';

Expand Down Expand Up @@ -32,6 +33,38 @@ export class Double extends BSONValue {
this.value = +value;
}

/**
* Attempt to create an double type from string.
*
* This method will throw a BSONError on any string input that is not representable as a IEEE-754 64-bit double.
* Notably, this method will also throw on the following string formats:
* - Strings in non-decimal formats (exponent notation, binary, hex, or octal digits)
* - Strings with characters other than numeric, floating point, or leading sign characters (Note: 'Infinity', '-Infinity', and 'NaN' input strings are still allowed)
* - Strings with leading and/or trailing whitespace
*
* Strings with leading zeros, however, are also allowed
*
* @param value - the string we want to represent as an double.
*/
static fromString(value: string): Double {
const coercedValue = Number(value);
const nonFiniteValidInputs = ['Infinity', '-Infinity', 'NaN'];

if (value.trim() !== value) {
throw new BSONError(`Input: '${value}' contains whitespace`);
} else if (value === '') {
throw new BSONError(`Input is an empty string`);
} else if (/[^-0-9.]/.test(value) && !nonFiniteValidInputs.includes(value)) {
throw new BSONError(`Input: '${value}' contains invalid characters`);
} else if (
(!Number.isFinite(coercedValue) && !nonFiniteValidInputs.includes(value)) ||
(Number.isNaN(coercedValue) && value !== 'NaN')
) {
throw new BSONError(`Input: ${value} is not representable as a Double`); // generic case
}
return new Double(coercedValue);
}

/**
* Access the number value.
*
Expand Down
50 changes: 50 additions & 0 deletions test/node/double.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,56 @@ describe('BSON Double Precision', function () {
});
});
});

describe('fromString', () => {
const acceptedInputs = [
['zero', '0', 0],
['non-leading zeros', '45000000', 45000000],
['zero with leading zeros', '000000.0000', 0],
['positive leading zeros', '000000867.1', 867.1],
['negative leading zeros', '-00007.980', -7.98],
['positive integer with decimal', '2.0', 2],
['zero with decimal', '0.0', 0.0],
['Infinity', 'Infinity', Infinity],
['-Infinity', '-Infinity', -Infinity],
['NaN', 'NaN', NaN],
['basic floating point', '-4.556000', -4.556],
['negative zero', '-0', -0]
];

const errorInputs = [
['commas', '34,450', 'contains invalid characters'],
['exponentiation notation', '1.34e16', 'contains invalid characters'],
['octal', '0o1', 'contains invalid characters'],
['binary', '0b1', 'contains invalid characters'],
['hex', '0x1', 'contains invalid characters'],
['empty string', '', 'is an empty string'],
['leading and trailing whitespace', ' 89 ', 'contains whitespace'],
['fake positive infinity', '2e308', 'contains invalid characters'],
['fake negative infinity', '-2e308', 'contains invalid characters'],
['fraction', '3/4', 'contains invalid characters'],
['foo', 'foo', 'contains invalid characters']
];

for (const [testName, value, expectedDouble] of acceptedInputs) {
context(`when the input is ${testName}`, () => {
it(`should successfully return a Double representation`, () => {
if (value === 'NaN') {
expect(isNaN(Double.fromString(value))).to.be.true;
} else {
expect(Double.fromString(value).value).to.equal(expectedDouble);
}
});
});
}
for (const [testName, value, expectedErrMsg] of errorInputs) {
context(`when the input is ${testName}`, () => {
it(`should throw an error containing '${expectedErrMsg}'`, () => {
expect(() => Double.fromString(value)).to.throw(BSON.BSONError, expectedErrMsg);
});
});
}
});
});

function serializeThenDeserialize(value) {
Expand Down

0 comments on commit e943cdb

Please sign in to comment.