misskey/src/api/it.ts
2017-03-02 21:54:46 +09:00

518 lines
13 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as mongo from 'mongodb';
import hasDuplicates from '../common/has-duplicates';
type Validator<T> = (value: T) => boolean | Error;
type Modifier<T> = (value: T) => T;
interface Factory {
get: () => [any, Error];
required: () => Factory;
validate: (validator: Validator<any>) => Factory;
modify: (modifier: Modifier<any>) => Factory;
}
class FactoryCore implements Factory {
value: any;
error: Error;
constructor() {
this.value = null;
this.error = null;
}
/**
* このインスタンスの値が undefined または null の場合エラーにします
*/
required() {
if (this.error === null && this.value === null) {
this.error = new Error('required');
}
return this;
}
/**
* このインスタンスの値およびエラーを取得します
*/
get(): [any, Error] {
return [this.value, this.error];
}
/**
* このインスタンスの値に対して妥当性を検証します
* バリデータが false またはエラーを返した場合エラーにします
* @param validator バリデータ
*/
validate(validator: Validator<any>) {
if (this.error || this.value === null) return this;
const result = validator(this.value);
if (result === false) {
this.error = new Error('invalid-format');
} else if (result instanceof Error) {
this.error = result;
}
return this;
}
modify(modifier: Modifier<any>) {
if (this.error || this.value === null) return this;
try {
this.value = modifier(this.value);
} catch (e) {
this.error = e;
}
return this;
}
}
class BooleanFactory extends FactoryCore {
value: boolean;
error: Error;
constructor(value) {
super();
if (value === undefined || value === null) {
this.value = null;
} else if (typeof value != 'boolean') {
this.error = new Error('must-be-a-boolean');
} else {
this.value = value;
}
}
/**
* このインスタンスの値が undefined または null の場合エラーにします
*/
required() {
return super.required();
}
/**
* このインスタンスの値およびエラーを取得します
*/
get(): [boolean, Error] {
return super.get();
}
/**
* このインスタンスの値に対して妥当性を検証します
* バリデータが false またはエラーを返した場合エラーにします
* @param validator バリデータ
*/
validate(validator: Validator<boolean>) {
return super.validate(validator);
}
modify(modifier: Modifier<boolean>) {
return super.modify(modifier);
}
}
class NumberFactory extends FactoryCore {
value: number;
error: Error;
constructor(value) {
super();
if (value === undefined || value === null) {
this.value = null;
} else if (!Number.isFinite(value)) {
this.error = new Error('must-be-a-number');
} else {
this.value = value;
}
}
/**
* 値が指定された範囲内にない場合エラーにします
* @param min 下限
* @param max 上限
*/
range(min: number, max: number) {
if (this.error || this.value === null) return this;
if (this.value < min || this.value > max) {
this.error = new Error('invalid-range');
}
return this;
}
/**
* このインスタンスの値が undefined または null の場合エラーにします
*/
required() {
return super.required();
}
/**
* このインスタンスの値およびエラーを取得します
*/
get(): [number, Error] {
return super.get();
}
/**
* このインスタンスの値に対して妥当性を検証します
* バリデータが false またはエラーを返した場合エラーにします
* @param validator バリデータ
*/
validate(validator: Validator<number>) {
return super.validate(validator);
}
modify(modifier: Modifier<number>) {
return super.modify(modifier);
}
}
class StringFactory extends FactoryCore {
value: string;
error: Error;
constructor(value) {
super();
if (value === undefined || value === null) {
this.value = null;
} else if (typeof value != 'string') {
this.error = new Error('must-be-a-string');
} else {
this.value = value;
}
}
/**
* 文字数が指定された範囲内にない場合エラーにします
* @param min 下限
* @param max 上限
*/
range(min: number, max: number) {
if (this.error || this.value === null) return this;
if (this.value.length < min || this.value.length > max) {
this.error = new Error('invalid-range');
}
return this;
}
trim() {
if (this.error || this.value === null) return this;
this.value = this.value.trim();
return this;
}
/**
* このインスタンスの値が undefined または null の場合エラーにします
*/
required() {
return super.required();
}
/**
* このインスタンスの値およびエラーを取得します
*/
get(): [string, Error] {
return super.get();
}
/**
* このインスタンスの値に対して妥当性を検証します
* バリデータが false またはエラーを返した場合エラーにします
* @param validator バリデータ
*/
validate(validator: Validator<string>) {
return super.validate(validator);
}
modify(modifier: Modifier<string>) {
return super.modify(modifier);
}
}
class ArrayFactory extends FactoryCore {
value: any[];
error: Error;
constructor(value) {
super();
if (value === undefined || value === null) {
this.value = null;
} else if (!Array.isArray(value)) {
this.error = new Error('must-be-an-array');
} else {
this.value = value;
}
}
/**
* 配列の値がユニークでない場合(=重複した項目がある場合)エラーにします
*/
unique() {
if (this.error || this.value === null) return this;
if (hasDuplicates(this.value)) {
this.error = new Error('must-be-unique');
}
return this;
}
/**
* 配列の長さが指定された範囲内にない場合エラーにします
* @param min 下限
* @param max 上限
*/
range(min: number, max: number) {
if (this.error || this.value === null) return this;
if (this.value.length < min || this.value.length > max) {
this.error = new Error('invalid-range');
}
return this;
}
/**
* このインスタンスの値が undefined または null の場合エラーにします
*/
required() {
return super.required();
}
/**
* このインスタンスの値およびエラーを取得します
*/
get(): [any[], Error] {
return super.get();
}
/**
* このインスタンスの値に対して妥当性を検証します
* バリデータが false またはエラーを返した場合エラーにします
* @param validator バリデータ
*/
validate(validator: Validator<any[]>) {
return super.validate(validator);
}
modify(modifier: Modifier<any[]>) {
return super.modify(modifier);
}
}
class IdFactory extends FactoryCore {
value: mongo.ObjectID;
error: Error;
constructor(value) {
super();
if (value === undefined || value === null) {
this.value = null;
} else if (typeof value != 'string' || !mongo.ObjectID.isValid(value)) {
this.error = new Error('must-be-an-id');
} else {
this.value = new mongo.ObjectID(value);
}
}
/**
* このインスタンスの値が undefined または null の場合エラーにします
*/
required() {
return super.required();
}
/**
* このインスタンスの値およびエラーを取得します
*/
get(): [any[], Error] {
return super.get();
}
/**
* このインスタンスの値に対して妥当性を検証します
* バリデータが false またはエラーを返した場合エラーにします
* @param validator バリデータ
*/
validate(validator: Validator<any[]>) {
return super.validate(validator);
}
modify(modifier: Modifier<any[]>) {
return super.modify(modifier);
}
}
class ObjectFactory extends FactoryCore {
value: any;
error: Error;
constructor(value) {
super();
if (value === undefined || value === null) {
this.value = null;
} else if (typeof value != 'object') {
this.error = new Error('must-be-an-object');
} else {
this.value = value;
}
}
/**
* このインスタンスの値が undefined または null の場合エラーにします
*/
required() {
return super.required();
}
/**
* このインスタンスの値およびエラーを取得します
*/
get(): [any, Error] {
return super.get();
}
/**
* このインスタンスの値に対して妥当性を検証します
* バリデータが false またはエラーを返した場合エラーにします
* @param validator バリデータ
*/
validate(validator: Validator<any>) {
return super.validate(validator);
}
modify(modifier: Modifier<any>) {
return super.modify(modifier);
}
}
type MustBe = {
must: {
be: {
a: {
string: () => StringFactory;
number: () => NumberFactory;
boolean: () => BooleanFactory;
};
an: {
id: () => IdFactory;
array: () => ArrayFactory;
object: () => ObjectFactory;
};
};
};
};
const mustBe = (value: any) => ({
must: {
be: {
a: {
string: () => new StringFactory(value),
number: () => new NumberFactory(value),
boolean: () => new BooleanFactory(value)
},
an: {
id: () => new IdFactory(value),
array: () => new ArrayFactory(value),
object: () => new ObjectFactory(value)
}
}
}
});
type Type = 'id' | 'string' | 'number' | 'boolean' | 'array' | 'set' | 'object';
type Pipe<T> = (x: T) => T | boolean | Error;
function validate(value: any, type: 'id', isRequired?: boolean, pipe?: Pipe<mongo.ObjectID> | Pipe<mongo.ObjectID>[]): [mongo.ObjectID, Error];
function validate(value: any, type: 'string', isRequired?: boolean, pipe?: Pipe<string> | Pipe<string>[]): [string, Error];
function validate(value: any, type: 'number', isRequired?: boolean, pipe?: Pipe<number> | Pipe<number>[]): [number, Error];
function validate(value: any, type: 'boolean', isRequired?: boolean): [boolean, Error];
function validate(value: any, type: 'array', isRequired?: boolean, pipe?: Pipe<any[]> | Pipe<any[]>[]): [any[], Error];
function validate(value: any, type: 'set', isRequired?: boolean, pipe?: Pipe<any[]> | Pipe<any[]>[]): [any[], Error];
function validate(value: any, type: 'object', isRequired?: boolean, pipe?: Pipe<any> | Pipe<any>[]): [any, Error];
function validate(value: any, type: Type, isRequired?: boolean, pipe?: Pipe<any> | Pipe<any>[]): [any, Error] {
if (value === undefined || value === null) {
if (isRequired) {
return [null, new Error('is-required')]
} else {
return [null, null]
}
}
switch (type) {
case 'id':
if (typeof value != 'string' || !mongo.ObjectID.isValid(value)) {
return [null, new Error('incorrect-id')];
}
break;
case 'string':
if (typeof value != 'string') {
return [null, new Error('must-be-a-string')];
}
break;
case 'number':
if (!Number.isFinite(value)) {
return [null, new Error('must-be-a-number')];
}
break;
case 'boolean':
if (typeof value != 'boolean') {
return [null, new Error('must-be-a-boolean')];
}
break;
case 'array':
if (!Array.isArray(value)) {
return [null, new Error('must-be-an-array')];
}
break;
case 'set':
if (!Array.isArray(value)) {
return [null, new Error('must-be-an-array')];
} else if (hasDuplicates(value)) {
return [null, new Error('duplicated-contents')];
}
break;
case 'object':
if (typeof value != 'object') {
return [null, new Error('must-be-an-object')];
}
break;
}
if (type == 'id') value = new mongo.ObjectID(value);
if (pipe) {
const pipes = Array.isArray(pipe) ? pipe : [pipe];
for (let i = 0; i < pipes.length; i++) {
const result = pipes[i](value);
if (result === false) {
return [null, new Error('invalid-format')];
} else if (result instanceof Error) {
return [null, result];
} else if (result !== true) {
value = result;
}
}
}
return [value, null];
}
function it(value: any): MustBe;
function it(value: any, type: 'id', isRequired?: boolean, pipe?: Pipe<mongo.ObjectID> | Pipe<mongo.ObjectID>[]): [mongo.ObjectID, Error];
function it(value: any, type: 'string', isRequired?: boolean, pipe?: Pipe<string> | Pipe<string>[]): [string, Error];
function it(value: any, type: 'number', isRequired?: boolean, pipe?: Pipe<number> | Pipe<number>[]): [number, Error];
function it(value: any, type: 'boolean', isRequired?: boolean): [boolean, Error];
function it(value: any, type: 'array', isRequired?: boolean, pipe?: Pipe<any[]> | Pipe<any[]>[]): [any[], Error];
function it(value: any, type: 'set', isRequired?: boolean, pipe?: Pipe<any[]> | Pipe<any[]>[]): [any[], Error];
function it(value: any, type: 'object', isRequired?: boolean, pipe?: Pipe<any> | Pipe<any>[]): [any, Error];
function it(value: any, type?: any, isRequired?: boolean, pipe?: Pipe<any> | Pipe<any>[]): any {
if (typeof type === 'undefined') {
return mustBe(value);
} else {
return validate(value, type, isRequired, pipe);
}
}
export default it;