import type { LoggerFriendly, LoggerFriendlyOptions } from "#Source/log/index.ts" import { Logger } from "#Source/log/index.ts" import type { Controller, Either, EitherAsyncGenerator, EitherSyncGenerator, GetEitherGeneratorResult, TupleOfEither, } from "#Source/result/index.ts" import { controllerFromEitherType, eitherToTuple, gen } from "#Source/result/index.ts" import type { ObjectPrettify } from "#Source/type/index.ts" export type ServiceRunContext = ObjectPrettify export type ServiceRunResult = Either export interface ServiceOptions extends LoggerFriendlyOptions {} /** * @description 通过继承此基类实现业务逻辑,与使用函数实现业务逻辑相比,具有以下优势: * 1. 依赖管理:统一注入所有通用依赖,如 logger 等,避免在每个函数实现中重复声明。 * 2. 暂时想不到了。 */ export abstract class Service< Context extends ServiceRunContext, Left, Right, > implements LoggerFriendly { readonly logger: Logger protected controller: Controller constructor(options: ServiceOptions) { this.logger = Logger.fromOptions(options).setDefaultName("Service") this.controller = controllerFromEitherType>() } protected gen, Return extends Either>( generatorFunction: (this: this) => EitherSyncGenerator, ): GetEitherGeneratorResult protected gen, Return extends Either>( generatorFunction: (this: this) => EitherAsyncGenerator, ): Promise> protected gen, Return extends Either>( generatorFunction: | ((this: this) => EitherSyncGenerator) | ((this: this) => EitherAsyncGenerator), ): GetEitherGeneratorResult | Promise> { return gen(generatorFunction as never, this) } protected loggerForRun(context: Context): Logger { const { logger } = context if (logger !== undefined) { return logger } else { return this.logger.derive().setDefaultName("Run") } } protected abstract run(context: Context): Promise> async runToEither(context: Context): Promise> { return await this.run(context) } async runToTuple(context: Context): Promise> { const result = await this.run(context) return eitherToTuple(result) } } export const isService = ( target: unknown, ): target is Service => { return target instanceof Service } // eslint-disable-next-line @typescript-eslint/no-explicit-any export type AnyServiceInstance = Service // eslint-disable-next-line @typescript-eslint/no-explicit-any export type AnyServiceConstructor = new (...args: any[]) => AnyServiceInstance export type GetServiceInstance = T extends new ( // eslint-disable-next-line @typescript-eslint/no-explicit-any ...args: any[] ) => infer Instance ? Instance : never export type GetServiceOptions = T extends new ( options: infer Options, // eslint-disable-next-line @typescript-eslint/no-explicit-any ...args: any[] // eslint-disable-next-line @typescript-eslint/no-explicit-any ) => any ? Options : never export type GetServiceRunContext = // eslint-disable-next-line @typescript-eslint/no-explicit-any GetServiceInstance extends Service ? Options : never export type GetServiceRunLeft = // eslint-disable-next-line @typescript-eslint/no-explicit-any GetServiceInstance extends Service ? Left : never export type GetServiceRunRight = // eslint-disable-next-line @typescript-eslint/no-explicit-any GetServiceInstance extends Service ? Right : never export const createService = ( ServiceConstructor: ServiceConstructor, constructorOptions: GetServiceOptions, ): GetServiceInstance => { return new ServiceConstructor(constructorOptions) as GetServiceInstance } export type RunToEither = ( runContext: ServiceRunContext>, ) => Promise, GetServiceRunRight>> export const createServiceRunToEither = ( ServiceConstructor: ServiceConstructor, constructorOptions: GetServiceOptions, ): RunToEither => { const service = createService(ServiceConstructor, constructorOptions) const run: RunToEither = async (runContext) => { const result = await service.runToEither(runContext) return result } return run } export type RunToTuple = ( runContext: ServiceRunContext>, ) => Promise< TupleOfEither, GetServiceRunRight> > export const createServiceRunToTuple = ( ServiceConstructor: ServiceConstructor, constructorOptions: GetServiceOptions, ): RunToTuple => { const service = createService(ServiceConstructor, constructorOptions) const run: RunToTuple = async (runContext) => { const result = await service.runToTuple(runContext) return result } return run } export const createServiceAndRunToEither = async ( ServiceConstructor: ServiceConstructor, constructorOptions: GetServiceOptions, runContext: ServiceRunContext>, ): Promise< Either, GetServiceRunRight> > => { const service = createService(ServiceConstructor, constructorOptions) const result = await service.runToEither(runContext) return result } export const createServiceAndRunToTuple = async ( ServiceConstructor: ServiceConstructor, constructorOptions: GetServiceOptions, runContext: ServiceRunContext>, ): Promise< TupleOfEither, GetServiceRunRight> > => { const service = createService(ServiceConstructor, constructorOptions) const result = await service.runToTuple(runContext) return result }