| 1 | import type { ProviderInstanceId } from "@t3tools/contracts"; |
| 2 | import * as Arr from "effect/Array"; |
| 3 | import * as Order from "effect/Order"; |
| 4 | |
| 5 | export interface ModelSlugItem { |
| 6 | readonly slug: string; |
| 7 | } |
| 8 | |
| 9 | export interface ProviderModelItem extends ModelSlugItem { |
| 10 | readonly instanceId: ProviderInstanceId; |
| 11 | } |
| 12 | |
| 13 | export function providerModelKey(instanceId: ProviderInstanceId, slug: string): string { |
| 14 | return `${instanceId}:${slug}`; |
| 15 | } |
| 16 | |
| 17 | function rankByValue(values: ReadonlyArray<string>): ReadonlyMap<string, number> { |
| 18 | return new Map(Arr.map(values, (value, index) => [value, index] as const)); |
| 19 | } |
| 20 | |
| 21 | function toSet( |
| 22 | values: ReadonlySet<string> | ReadonlyArray<string> | undefined, |
| 23 | ): ReadonlySet<string> { |
| 24 | return values instanceof Set ? values : new Set(values ?? []); |
| 25 | } |
| 26 | |
| 27 | function byOptionalRank<T>(rank: (item: T) => number | undefined): Order.Order<T> { |
| 28 | return Order.mapInput(Order.Number, (item: T) => rank(item) ?? Number.POSITIVE_INFINITY); |
| 29 | } |
| 30 | |
| 31 | function byTrueFirst<T>(predicate: (item: T) => boolean): Order.Order<T> { |
| 32 | return Order.mapInput(Order.flip(Order.Boolean), predicate); |
| 33 | } |
| 34 | |
| 35 | export function sortModelsForProviderInstance<T extends ModelSlugItem>( |
| 36 | models: ReadonlyArray<T>, |
| 37 | options?: { |
| 38 | readonly modelOrder?: ReadonlyArray<string>; |
| 39 | readonly favoriteModels?: ReadonlySet<string> | ReadonlyArray<string>; |
| 40 | readonly groupFavorites?: boolean; |
| 41 | }, |
| 42 | ): T[] { |
| 43 | const modelOrder = options?.modelOrder ?? []; |
| 44 | const favoriteModels = toSet(options?.favoriteModels); |
| 45 | const orderBySlug = rankByValue(modelOrder); |
| 46 | const originalOrder = rankByValue(Arr.map(models, (model) => model.slug)); |
| 47 | const orders: Array<Order.Order<T>> = [ |
| 48 | ...(options?.groupFavorites === true |
| 49 | ? [byTrueFirst<T>((model) => favoriteModels.has(model.slug))] |
| 50 | : []), |
| 51 | byOptionalRank((model) => orderBySlug.get(model.slug)), |
| 52 | byOptionalRank((model) => originalOrder.get(model.slug)), |
| 53 | ]; |
| 54 | |
| 55 | return Arr.sort(models, Order.combineAll(orders)); |
| 56 | } |
| 57 | |
| 58 | export function sortProviderModelItems<T extends ProviderModelItem>( |
| 59 | items: ReadonlyArray<T>, |
| 60 | options?: { |
| 61 | readonly favoriteModelKeys?: ReadonlySet<string> | ReadonlyArray<string>; |
| 62 | readonly groupFavorites?: boolean; |
| 63 | readonly instanceOrder?: ReadonlyArray<ProviderInstanceId>; |
| 64 | }, |
| 65 | ): T[] { |
| 66 | const favoriteModelKeys = toSet(options?.favoriteModelKeys); |
| 67 | const instanceOrder = new Map( |
| 68 | Arr.map(options?.instanceOrder ?? [], (instanceId, index) => [instanceId, index] as const), |
| 69 | ); |
| 70 | const originalOrder = rankByValue( |
| 71 | Arr.map(items, (item) => providerModelKey(item.instanceId, item.slug)), |
| 72 | ); |
| 73 | const orders: Array<Order.Order<T>> = [ |
| 74 | ...(options?.groupFavorites === true |
| 75 | ? [ |
| 76 | byTrueFirst<T>((item) => |
| 77 | favoriteModelKeys.has(providerModelKey(item.instanceId, item.slug)), |
| 78 | ), |
| 79 | ] |
| 80 | : []), |
| 81 | byOptionalRank((item) => instanceOrder.get(item.instanceId)), |
| 82 | byOptionalRank((item) => originalOrder.get(providerModelKey(item.instanceId, item.slug))), |
| 83 | ]; |
| 84 | |
| 85 | return Arr.sort(items, Order.combineAll(orders)); |
| 86 | } |