Skip to content

Commit

Permalink
Merge pull request #1486 from actnwit/fix/option
Browse files Browse the repository at this point in the history
fix: option
  • Loading branch information
emadurandal authored Nov 28, 2024
2 parents a022b71 + a1665e6 commit c48ed6a
Show file tree
Hide file tree
Showing 6 changed files with 411 additions and 144 deletions.
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

0 comments on commit c48ed6a

Please sign in to comment.