@deepkit/bson
Version:
Deepkit BSON parser
174 lines (144 loc) • 6.21 kB
text/typescript
/*
* Deepkit Framework
* Copyright (C) 2021 Deepkit UG, Marc J. Schmidt
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the MIT License.
*
* You should have received a copy of the MIT License along with this program.
*/
import { isValidEnumValue } from '@deepkit/core';
import { PropertySchema, typedArrayNamesMap, UnionGuardsTypes } from '@deepkit/type';
import { BaseParser } from './bson-parser';
import { findValueInObject } from './continuation';
import { BSONType } from './utils';
export type BSONTypeGuard = (elementType: BSONType, parser: BaseParser) => boolean;
export type BSONTypeGuardFactory = (property: PropertySchema) => BSONTypeGuard;
export const bsonTypeGuards = new Map<UnionGuardsTypes, BSONTypeGuardFactory>();
export function registerBSONTypeGuard(type: UnionGuardsTypes, factory: BSONTypeGuardFactory) {
bsonTypeGuards.set(type, factory);
}
registerBSONTypeGuard('class', (property: PropertySchema) => {
const schema = property.getResolvedClassSchema();
if (schema.discriminant) {
const discriminant = schema.getProperty(schema.discriminant);
const discriminantValue = discriminant.type === 'literal' ? discriminant.literalValue :
discriminant.defaultValue ? discriminant.defaultValue() : undefined;
if (discriminantValue === undefined) {
throw new Error(`Discriminant ${schema.getClassPropertyName(discriminant.name)} has no default value.`);
}
const checker = (elementType: BSONType, name: string) => {
return name === discriminant.name && (elementType !== BSONType.OBJECT);
}
return (elementType: BSONType, parser: BaseParser) => {
if (elementType !== BSONType.OBJECT) return false;
const v = findValueInObject(parser, checker);
return v === discriminantValue;
};
}
//we need to figure out what could be the discriminant
for (const property of schema.getProperties()) {
if (property.type !== 'literal') continue;
const checker = (elementType: BSONType, name: string) => {
return name === property.name && (elementType !== BSONType.OBJECT);
}
return (elementType: BSONType, parser: BaseParser) => {
if (elementType !== BSONType.OBJECT) return false;
const v = findValueInObject(parser, checker);
return v === property.literalValue;
};
}
throw new Error(`Type of property ${property.name} (${property.toString()}, ${schema.getClassName()}) has no discriminant or literal set. Could not discriminate the value.`);
});
registerBSONTypeGuard('string', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.STRING;
};
});
registerBSONTypeGuard('enum', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
const validType = elementType === BSONType.STRING || elementType === BSONType.NUMBER;
if (validType) {
const v = parser.peek(elementType, property);
return undefined !== v && !isValidEnumValue(property.resolveClassType, v, property.allowLabelsAsValue);
}
return false;
};
});
registerBSONTypeGuard('objectId', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.OID;
};
});
registerBSONTypeGuard('uuid', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.BINARY;
};
});
registerBSONTypeGuard('arrayBuffer', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.BINARY;
};
});
function typedArrayGuard(property: PropertySchema) {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.BINARY;
};
}
for (const name of typedArrayNamesMap.keys()) {
registerBSONTypeGuard(name, typedArrayGuard);
}
registerBSONTypeGuard('date', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.DATE;
};
});
registerBSONTypeGuard('any', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return true;
};
});
registerBSONTypeGuard('union', (property: PropertySchema) => {
throw new Error('Union typechecking not implemented. Nested unions thus not supported yet.');
});
registerBSONTypeGuard('array', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.ARRAY;
};
});
registerBSONTypeGuard('map', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.OBJECT;
};
});
registerBSONTypeGuard('patch', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.OBJECT;
};
});
registerBSONTypeGuard('partial', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.OBJECT;
};
});
registerBSONTypeGuard('number', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.NUMBER || elementType === BSONType.INT || elementType === BSONType.LONG;
};
});
registerBSONTypeGuard('bigint', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.BINARY;
};
});
registerBSONTypeGuard('boolean', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
return elementType === BSONType.BOOLEAN;
};
});
registerBSONTypeGuard('literal', (property: PropertySchema) => {
return (elementType: BSONType, parser: BaseParser) => {
const v = parser.peek(elementType);
return v === property.literalValue;
};
});