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

fix: option #1486

Merged
merged 11 commits into from
Nov 28, 2024
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: 1 addition & 3 deletions src/foundation/core/EntityRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,7 @@ export class EntityRepository {
const map = valueWithCompensation({
value: this._components[entity.entityUID],
compensation: () => {
const map = new Map();
this._components[entity.entityUID] = map;
return map;
return (this._components[entity.entityUID] = new Map());
},
});
map.set(componentClass.componentTID, component);
Expand Down
6 changes: 3 additions & 3 deletions src/foundation/geometry/Primitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
RaycastResult,
RaycastResultEx1,
} from './types/GeometryTypes';
import { IOption, None, Some, Option } from '../misc/Option';
import { Option, None, Some } from '../misc/Option';
import { DataUtil } from '../misc/DataUtil';
import { Config } from '../core/Config';
import { isErr } from '../misc/Result';
Expand Down Expand Up @@ -62,7 +62,7 @@ export class Primitive extends RnObject {
private __currentVariantName = '';
public _prevMaterial: WeakRef<Material>;
private __attributes: Attributes = new Map();
private __oIndices: IOption<Accessor> = new None();
private __oIndices: Option<Accessor> = new None();
private static __primitiveCount: Count = 0;
private __primitiveUid: PrimitiveUID = -1; // start ID from zero
private __aabb = new AABB();
Expand Down Expand Up @@ -267,7 +267,7 @@ export class Primitive extends RnObject {
material?: Material,
indicesAccessor?: Accessor
) {
this.__oIndices = new Option(indicesAccessor);
this.__oIndices = new Some(indicesAccessor!);
this.__attributes = attributes;

const positionAccessor = this.__attributes.get(VertexAttribute.Position.XYZ)!;
Expand Down
322 changes: 301 additions & 21 deletions src/foundation/misc/Option.test.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,81 @@
import { IOption, None, Option, Some } from './Option';
import { Option, None, Some } from './Option';
test('unwrapOrDefault', () => {
const val0: Option<number> = new Some(0);
expect(val0.unwrapOrDefault(1)).toEqual(0);

test('Basic usage of Option', () => {
const val: Option<number> = new Option();
expect(val.unwrapOrDefault(0)).toEqual(0);
const val1: Option<number> = new None();
expect(val1.unwrapOrDefault(1)).toEqual(1);
});

test('unwrapOrUndefined', () => {
const val0: Option<number> = new Some(0);
expect(val0.unwrapOrUndefined()).toEqual(0);

const val1: Option<number> = new None();
expect(val1.unwrapOrUndefined()).toEqual(undefined);
});

test('unwrapOrElse', () => {
const val0: Option<number> = new Some(0);
expect(
val.unwrapOrElse(() => {
val0.unwrapOrElse(() => {
return 100;
})
).toEqual(100);
).toEqual(0);

const val1: Option<number> = new None();
expect(
val1.unwrapOrElse(() => {
return 1;
})
).toEqual(1);
});

// throw error
test('unwrapForce', () => {
const val0: Option<number> = new Some(0);
expect(val0.unwrapForce()).toEqual(0);

const val1: Option<number> = new None();
expect(() => {
val.unwrapForce();
// throw error
val1.unwrapForce();
}).toThrowError(ReferenceError);
});

// now set a real value
val.set(10);
test('has and get', () => {
const val0: Option<number> = new Some(0);
expect(val0.has()).toBe(true);

expect(val.unwrapOrDefault(0)).toEqual(10);
expect(
val.unwrapOrElse(() => {
return 100;
})
).toEqual(10);
expect(val.unwrapForce()).toEqual(10);
const val1: Option<number> = new None();
expect(val1.has()).toBe(false);

const val2: Option<number> = new Some(1);
if (val2.has()) {
expect(val2.get()).toEqual(1);
} else {
fail('val2 should have a value');
}
});

test('doesNotHave', () => {
const val0: Option<number> = new Some(0);
expect(val0.doesNotHave()).toBe(false);

const val1: Option<number> = new None();
expect(val1.doesNotHave()).toBe(true);

const val2: Option<number> = new None();
if (val2.doesNotHave()) {
expect(val2.has()).toBe(false);
} else {
fail('val2 should not have a value');
}
});

test('Basic usage of Some and None', () => {
class Hit {}

const funcUsingSomeAndNone = (val: number): IOption<Hit> => {
const funcUsingSomeAndNone = (val: number): Option<Hit> => {
if (val % 2 === 0) {
// return hit object for even numbers
return new Some(new Hit());
Expand All @@ -39,18 +85,252 @@ test('Basic usage of Some and None', () => {
}
};

const result0: IOption<Hit> = funcUsingSomeAndNone(0); // Some
const result1: IOption<Hit> = funcUsingSomeAndNone(1); // None
const result0: Option<Hit> = funcUsingSomeAndNone(0); // Some
const result1: Option<Hit> = funcUsingSomeAndNone(1); // None
expect(result0.unwrapForce().constructor.name).toBe(Hit.name);
expect(() => {
result1.unwrapForce().constructor.name;
}).toThrowError(ReferenceError);
});

test('An IOption variable can be replaced by Some', () => {
let val: IOption<number> = new None();
let val: Option<number> = new None();
val = new Some(10);
const valRaw = val.unwrapForce();

expect(valRaw).toEqual(10);
});

test('then(func)', () => {
const val0: Option<number> = new Some(0);
val0.then((val) => {
expect(val).toEqual(0);
});
});

test('const var = then(func)', () => {
const val0: Option<number> = new Some(0);
const val1: Option<number> = val0.then((val) => {
return new Some(val);
});
expect(val1.has()).toBe(true);

const none: Option<number> = new None();
const none2: Option<number> = none.then((val) => {
return new Some(val); // this is not executed
});
expect(none2.doesNotHave()).toBe(true);
});

test('then<T>', () => {
const val0: Option<number> = new Some(0);
const val1: Option<string> = val0.then<string>((_val) => {
return new Some('exist!');
});
expect(val1.unwrapForce()).toEqual('exist!');
});

test('else(func)', () => {
const val0: Option<number> = new None();
val0.else(() => {
expect(val0.doesNotHave()).toBe(true);
});
});

test('const var = else(func)', () => {
const val0: Option<number> = new None();
const val1: Option<number> = val0.else(() => {
return new Some(1);
});
expect(val1.has()).toBe(true);

const none: Option<number> = new Some(0);
const none2: Option<number> = none.else(() => {
return new Some(1); // this is not executed
});
expect(none2.unwrapForce()).toEqual(0);
});

test('then(func).else(func)', () => {
{
const some: Option<number> = new Some(0);
const val = some
.then((val) => {
// this is executed because a some.then() does the received function
expect(val).toEqual(0);
})
.else(() => {
console.error(
'this is not executed. because the Some(0).else() just returns the Some(0) itself but execute received function.'
);
});
expect(val.unwrapForce()).toEqual(0);
}
{
const none: Option<number> = new None();
const val = none
.then(() => {
console.error('this is not executed. because a none.then() just returns the none itself');
})
.else(() => {
return new Some(1); // this is executed because a none.else() execute the received function
});
expect(val.unwrapForce()).toEqual(1);
}
{
const none: Option<number> = new None();
const val = none
.then(() => {
console.error('this is not executed. because a none.then() just returns the none itself');
})
.else(() => {
// nothing to do
// none.else() returns the none itself
});
expect(val.doesNotHave()).toBe(true);
}
});

test('else(func).then(func)', () => {
{
const some: Option<number> = new Some(0);
const val = some
.else(() => {
console.error('this is not executed');
})
.then((val) => {
expect(val).toEqual(0); // do something with the original value
return new Some(val);
});
expect(val.unwrapForce()).toEqual(0);
}
{
const none: Option<number> = new None();
const val = none
.else(() => {
return new Some(1); // recover
})
.then((val) => {
expect(val).toEqual(1); // do something with the recovered value
});
expect(val.unwrapForce()).toEqual(1);
}
});

test('then(func).else(func) with return value', () => {
{
const some: Option<number> = new Some(0);
const val = some
.then((val) => {
// this is executed because a some.then() does the received function
expect(val).toEqual(0);
return new Some(val);
})
.else(() => {
console.error(
'this is not executed. because the Some(val).else() just returns the Some(val) itself but execute received function.'
);
return new Some(1);
});
expect(val.unwrapForce()).toEqual(0);
}
{
const none: Option<number> = new None();
const val = none
.then((val) => {
console.error(
'this is not executed. because a none.then() just returns the none itself but execute received function.'
);
expect(val).toEqual(0);
return new Some(val);
})
.else(() => {
return new Some(1); // this is executed because a none.else() execute the received function
});
expect(val.unwrapForce()).toEqual(1);
}
});

test('else(func).then(func) with return value', () => {
{
const some: Option<number> = new Some(0);
const val = some
.else(() => {
console.error('this is not executed');
return new Some(1); // recover
})
.then((val) => {
expect(val).toEqual(0); // do something with the original value
return new Some(val);
});
expect(val.unwrapForce()).toEqual(0);
}
{
const none: Option<number> = new None();
const val = none
.else(() => {
return new Some(1); // recover
})
.then((val) => {
expect(val).toEqual(1); // do something with the recovered value
return new Some(val);
});
expect(val.unwrapForce()).toEqual(1);
}
});

test('match', () => {
const some: Option<number> = new Some(0);
const val = some.match({
Some: (val) => {
expect(val).toEqual(0);
},
None: () => {
fail('this is not executed');
},
});
expect(val).toBeUndefined();

const none: Option<number> = new None();
const val2 = none.match({
Some: (_val) => {
fail('this is not executed');
},
None: () => {
expect(none.doesNotHave()).toBe(true);
},
});
expect(val2).toBeUndefined();
});

test('match with return value', () => {
const some: Option<number> = new Some(0);
const val = some.match({
Some: (val) => val,
None: () => 1,
});
expect(val).toEqual(0);

const none: Option<number> = new None();
const val2 = none.match({
Some: (val) => val,
None: () => 1,
});
expect(val2).toEqual(1);
});

test('match<T> with return value', () => {
const some: Option<number> = new Some(0);
const val = some.match({
Some: (_val) => 'exist!',
None: () => 'none',
});
expect(val).toEqual('exist!');

const none: Option<number> = new None();
const val2 = none.match({
Some: (_val) => 'exist!',
None: () => 'none',
});
expect(val2).toEqual('none');
});
Loading
Loading