1import type { ProviderInstanceId } from "@t3tools/contracts";
2import * as Arr from "effect/Array";
3import * as Order from "effect/Order";
4
5export interface ModelSlugItem {
6 readonly slug: string;
7}
8
9export interface ProviderModelItem extends ModelSlugItem {
10 readonly instanceId: ProviderInstanceId;
11}
12
13export function providerModelKey(instanceId: ProviderInstanceId, slug: string): string {
14 return `${instanceId}:${slug}`;
15}
16
17function rankByValue(values: ReadonlyArray<string>): ReadonlyMap<string, number> {
18 return new Map(Arr.map(values, (value, index) => [value, index] as const));
19}
20
21function toSet(
22 values: ReadonlySet<string> | ReadonlyArray<string> | undefined,
23): ReadonlySet<string> {
24 return values instanceof Set ? values : new Set(values ?? []);
25}
26
27function 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
31function byTrueFirst<T>(predicate: (item: T) => boolean): Order.Order<T> {
32 return Order.mapInput(Order.flip(Order.Boolean), predicate);
33}
34
35export 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
58export 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}