Skip to content

Commit a9589b6

Browse files
authored
feat: More scalars (#50)
1 parent fbb7c94 commit a9589b6

File tree

11 files changed

+615
-31
lines changed

11 files changed

+615
-31
lines changed

src/scalar/date.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { DataType, Date_ as ArrowDate, DateUnit } from '@apache-arrow/esnext-esm';
2+
import { DateTime } from 'luxon';
3+
4+
import { Scalar } from './scalar.js';
5+
import { isInvalid, NULL_VALUE } from './util.js';
6+
7+
export class Date implements Scalar<DateTime> {
8+
private _valid = false;
9+
private _value: DateTime = DateTime.fromMillis(0);
10+
private _unit: DateUnit;
11+
12+
public constructor(unit: DateUnit, v?: unknown) {
13+
this._unit = unit;
14+
this.value = v;
15+
return this;
16+
}
17+
18+
public get dataType(): DataType {
19+
return new ArrowDate(this._unit);
20+
}
21+
22+
public get valid(): boolean {
23+
return this._valid;
24+
}
25+
26+
public get value(): DateTime {
27+
return this._value;
28+
}
29+
30+
public set value(value: unknown) {
31+
if (isInvalid(value)) {
32+
this._valid = false;
33+
return;
34+
}
35+
36+
if (value instanceof Date && value.dataType === this.dataType) {
37+
this._valid = value.valid;
38+
this._value = value.value;
39+
return;
40+
}
41+
42+
let dateValue: DateTime | null = null;
43+
44+
if (typeof value === 'string') {
45+
dateValue = DateTime.fromISO(value, { setZone: true });
46+
47+
if (!dateValue.isValid) {
48+
dateValue = DateTime.fromFormat(value, 'yyyy-MM-dd', { zone: 'utc' });
49+
}
50+
}
51+
52+
if (dateValue && dateValue.isValid) {
53+
this._value = dateValue;
54+
this._valid = true;
55+
return;
56+
}
57+
58+
throw new Error(`Unable to set '${value}' as Date`);
59+
}
60+
61+
public toString(): string {
62+
if (this._valid) {
63+
return this._value.toISO()!;
64+
}
65+
66+
return NULL_VALUE;
67+
}
68+
}

src/scalar/float32.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { DataType, Float32 as ArrowFloat32 } from '@apache-arrow/esnext-esm';
2+
3+
import { Scalar } from './scalar.js';
4+
import { isInvalid, NULL_VALUE } from './util.js';
5+
6+
export class Float32 implements Scalar<number> {
7+
private _valid = false;
8+
private _value: number = 0;
9+
10+
public constructor(v?: unknown) {
11+
this.value = v;
12+
return this;
13+
}
14+
15+
public get dataType(): DataType {
16+
return new ArrowFloat32();
17+
}
18+
19+
public get valid(): boolean {
20+
return this._valid;
21+
}
22+
23+
public get value(): number {
24+
return this._value;
25+
}
26+
27+
public set value(value: unknown) {
28+
if (isInvalid(value)) {
29+
this._valid = false;
30+
return;
31+
}
32+
33+
if (value instanceof Float32) {
34+
this._valid = value.valid;
35+
this._value = value.value;
36+
return;
37+
}
38+
39+
if (typeof value === 'number') {
40+
if (!this.validFloat32(value)) {
41+
throw new TypeError(`Value '${value}' cannot be safely converted to Float32`);
42+
}
43+
44+
this._value = value;
45+
this._valid = true;
46+
return;
47+
}
48+
49+
const floatValue = Number.parseFloat(String(value));
50+
if (!Number.isNaN(floatValue)) {
51+
if (!this.validFloat32(floatValue)) {
52+
throw new TypeError(`Value '${value}' cannot be safely converted to Float32`);
53+
}
54+
55+
this._value = floatValue;
56+
this._valid = true;
57+
return;
58+
}
59+
60+
throw new Error(`Unable to set '${value}' as Float32`);
61+
}
62+
63+
public toString() {
64+
if (this._valid) {
65+
return String(this._value);
66+
}
67+
68+
return NULL_VALUE;
69+
}
70+
71+
validFloat32(n: number) {
72+
const float32 = new Float32Array(1);
73+
float32[0] = n;
74+
return float32[0] === n;
75+
}
76+
}

src/scalar/int16.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { DataType, Int16 as ArrowInt16 } from '@apache-arrow/esnext-esm';
2+
import { bigIntToNumber } from '@apache-arrow/esnext-esm/util/bigint.js';
3+
4+
import { Scalar } from './scalar.js';
5+
import { isInvalid, NULL_VALUE } from './util.js';
6+
7+
export class Int16 implements Scalar<bigint> {
8+
private _valid = false;
9+
private _value: bigint = BigInt(0);
10+
11+
public constructor(v?: unknown) {
12+
this.value = v;
13+
return this;
14+
}
15+
16+
public get dataType(): DataType {
17+
return new ArrowInt16();
18+
}
19+
20+
public get valid(): boolean {
21+
return this._valid;
22+
}
23+
24+
public get value(): bigint {
25+
return this._value;
26+
}
27+
28+
public set value(value: unknown) {
29+
if (isInvalid(value)) {
30+
this._valid = false;
31+
return;
32+
}
33+
34+
if (value instanceof Int16) {
35+
this._valid = value.valid;
36+
this._value = value.value;
37+
return;
38+
}
39+
40+
if (typeof value === 'bigint') {
41+
if (!this.validInt16(value)) {
42+
throw new TypeError(`Value '${value}' cannot be safely converted to Int16`);
43+
}
44+
this._value = value;
45+
this._valid = true;
46+
return;
47+
}
48+
49+
if (typeof value === 'number') {
50+
const v = BigInt(value);
51+
if (!this.validInt16(v)) {
52+
throw new TypeError(`Value '${value}' cannot be safely converted to Int16`);
53+
}
54+
this._value = v;
55+
this._valid = true;
56+
return;
57+
}
58+
59+
throw new Error(`Unable to set '${value}' as Int16`);
60+
}
61+
62+
public toString() {
63+
if (this._valid) {
64+
return String(this._value);
65+
}
66+
67+
return NULL_VALUE;
68+
}
69+
70+
validInt16(n: bigint) {
71+
const number_ = bigIntToNumber(n);
72+
return Number.isSafeInteger(number_) && number_ >= -32_768 && number_ <= 32_767;
73+
}
74+
}

src/scalar/int32.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { DataType, Int32 as ArrowInt32 } from '@apache-arrow/esnext-esm';
2+
import { bigIntToNumber } from '@apache-arrow/esnext-esm/util/bigint.js';
3+
4+
import { Scalar } from './scalar.js';
5+
import { isInvalid, NULL_VALUE } from './util.js';
6+
7+
export class Int32 implements Scalar<bigint> {
8+
private _valid = false;
9+
private _value: bigint = BigInt(0);
10+
11+
public constructor(v?: unknown) {
12+
this.value = v;
13+
return this;
14+
}
15+
16+
public get dataType(): DataType {
17+
return new ArrowInt32();
18+
}
19+
20+
public get valid(): boolean {
21+
return this._valid;
22+
}
23+
24+
public get value(): bigint {
25+
return this._value;
26+
}
27+
28+
public set value(value: unknown) {
29+
if (isInvalid(value)) {
30+
this._valid = false;
31+
return;
32+
}
33+
34+
if (value instanceof Int32) {
35+
this._valid = value.valid;
36+
this._value = value.value;
37+
return;
38+
}
39+
40+
if (typeof value === 'bigint') {
41+
if (!this.validInt32(value)) {
42+
throw new TypeError(`Value '${value}' cannot be safely converted to Int32`);
43+
}
44+
this._value = value;
45+
this._valid = true;
46+
return;
47+
}
48+
49+
if (typeof value === 'number') {
50+
const v = BigInt(value);
51+
if (!this.validInt32(v)) {
52+
throw new TypeError(`Value '${value}' cannot be safely converted to Int32`);
53+
}
54+
this._value = v;
55+
this._valid = true;
56+
return;
57+
}
58+
59+
throw new Error(`Unable to set '${value}' as Int32`);
60+
}
61+
62+
public toString() {
63+
if (this._valid) {
64+
return String(this._value);
65+
}
66+
67+
return NULL_VALUE;
68+
}
69+
70+
validInt32(n: bigint) {
71+
const number_ = bigIntToNumber(n);
72+
return Number.isSafeInteger(number_) && number_ >= -2_147_483_648 && number_ <= 2_147_483_647;
73+
}
74+
}

src/scalar/int64.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { DataType, Int64 as ArrowInt64 } from '@apache-arrow/esnext-esm';
2+
import { bigIntToNumber } from '@apache-arrow/esnext-esm/util/bigint.js';
23

34
import { Scalar } from './scalar.js';
45
import { isInvalid, NULL_VALUE } from './util.js';
@@ -37,16 +38,20 @@ export class Int64 implements Scalar<bigint> {
3738
}
3839

3940
if (typeof value === 'bigint') {
41+
if (!this.validInt64(value)) {
42+
throw new TypeError(`Value '${value}' cannot be safely converted to Int64`);
43+
}
4044
this._value = value;
4145
this._valid = true;
4246
return;
4347
}
4448

4549
if (typeof value === 'number') {
46-
if (!Number.isSafeInteger(value)) {
50+
const v = BigInt(value);
51+
if (!this.validInt64(v)) {
4752
throw new TypeError(`Value '${value}' cannot be safely converted to Int64`);
4853
}
49-
this._value = BigInt(value);
54+
this._value = v;
5055
this._valid = true;
5156
return;
5257
}
@@ -61,4 +66,10 @@ export class Int64 implements Scalar<bigint> {
6166

6267
return NULL_VALUE;
6368
}
69+
70+
validInt64(n: bigint) {
71+
const MIN_INT64 = BigInt('-9223372036854775808'); // -2^63
72+
const MAX_INT64 = BigInt('9223372036854775807'); // 2^63 - 1
73+
return Number.isSafeInteger(bigIntToNumber(n)) && n >= MIN_INT64 && n <= MAX_INT64;
74+
}
6475
}

src/scalar/list.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,39 @@ import { Int64 } from './int64.js';
44
import { List } from './list.js';
55

66
test('list', (t) => {
7-
const l = new List(Int64);
8-
t.deepEqual(l, new List(Int64));
7+
const l = new List(new Int64());
8+
t.deepEqual(l, new List(new Int64()));
99

1010
l.value = [new Int64(17), new Int64(19), new Int64(null)];
1111
t.is(l.length, 3);
1212
});
1313

1414
test('list equality', (t) => {
15-
const l1 = new List(Int64);
15+
const l1 = new List(new Int64());
1616
l1.value = [new Int64(17), new Int64(19), new Int64(null)];
1717

18-
const l2 = new List(Int64);
18+
const l2 = new List(new Int64());
1919
l2.value = [new Int64(17), new Int64(19), new Int64(null)];
2020

2121
t.deepEqual(l1, l2);
2222
});
2323

2424
test('list inequality', (t) => {
25-
const l1 = new List(Int64);
25+
const l1 = new List(new Int64());
2626
l1.value = new Int64(4);
2727

28-
const l2 = new List(Int64);
28+
const l2 = new List(new Int64());
2929
l2.value = new Int64(7);
3030

3131
t.notDeepEqual(l1, l2);
3232
});
3333

3434
test('list equality when invalid', (t) => {
35-
const l1 = new List(Int64);
36-
l1.value = new Int64(null);
35+
const l1 = new List(new Int64());
36+
l1.value = new Int64();
3737

38-
const l2 = new List(Int64);
39-
l2.value = new Int64(null);
38+
const l2 = new List(new Int64());
39+
l2.value = new Int64();
4040

4141
t.deepEqual(l1, l2);
4242
});

0 commit comments

Comments
 (0)