import { MongoInvalidArgumentError } from './error'; /** @public */ export type SortDirection = | 1 | -1 | 'asc' | 'desc' | 'ascending' | 'descending' | { $meta: string }; /** @public */ export type Sort = | string | Exclude | string[] | { [key: string]: SortDirection } | Map | [string, SortDirection][] | [string, SortDirection]; /** Below stricter types were created for sort that correspond with type that the cmd takes */ /** @internal */ export type SortDirectionForCmd = 1 | -1 | { $meta: string }; /** @internal */ export type SortForCmd = Map; /** @internal */ type SortPairForCmd = [string, SortDirectionForCmd]; /** @internal */ function prepareDirection(direction: any = 1): SortDirectionForCmd { const value = `${direction}`.toLowerCase(); if (isMeta(direction)) return direction; switch (value) { case 'ascending': case 'asc': case '1': return 1; case 'descending': case 'desc': case '-1': return -1; default: throw new MongoInvalidArgumentError(`Invalid sort direction: ${JSON.stringify(direction)}`); } } /** @internal */ function isMeta(t: SortDirection): t is { $meta: string } { return typeof t === 'object' && t != null && '$meta' in t && typeof t.$meta === 'string'; } /** @internal */ function isPair(t: Sort): t is [string, SortDirection] { if (Array.isArray(t) && t.length === 2) { try { prepareDirection(t[1]); return true; } catch (e) { return false; } } return false; } function isDeep(t: Sort): t is [string, SortDirection][] { return Array.isArray(t) && Array.isArray(t[0]); } function isMap(t: Sort): t is Map { return t instanceof Map && t.size > 0; } /** @internal */ function pairToMap(v: [string, SortDirection]): SortForCmd { return new Map([[`${v[0]}`, prepareDirection([v[1]])]]); } /** @internal */ function deepToMap(t: [string, SortDirection][]): SortForCmd { const sortEntries: SortPairForCmd[] = t.map(([k, v]) => [`${k}`, prepareDirection(v)]); return new Map(sortEntries); } /** @internal */ function stringsToMap(t: string[]): SortForCmd { const sortEntries: SortPairForCmd[] = t.map(key => [`${key}`, 1]); return new Map(sortEntries); } /** @internal */ function objectToMap(t: { [key: string]: SortDirection }): SortForCmd { const sortEntries: SortPairForCmd[] = Object.entries(t).map(([k, v]) => [ `${k}`, prepareDirection(v) ]); return new Map(sortEntries); } /** @internal */ function mapToMap(t: Map): SortForCmd { const sortEntries: SortPairForCmd[] = Array.from(t).map(([k, v]) => [ `${k}`, prepareDirection(v) ]); return new Map(sortEntries); } /** converts a Sort type into a type that is valid for the server (SortForCmd) */ export function formatSort( sort: Sort | undefined, direction?: SortDirection ): SortForCmd | undefined { if (sort == null) return undefined; if (typeof sort === 'string') return new Map([[sort, prepareDirection(direction)]]); if (typeof sort !== 'object') { throw new MongoInvalidArgumentError( `Invalid sort format: ${JSON.stringify(sort)} Sort must be a valid object` ); } if (!Array.isArray(sort)) { return isMap(sort) ? mapToMap(sort) : Object.keys(sort).length ? objectToMap(sort) : undefined; } if (!sort.length) return undefined; if (isDeep(sort)) return deepToMap(sort); if (isPair(sort)) return pairToMap(sort); return stringsToMap(sort); }