import { Mail } from "./mail.ts"; class MailDeliverError extends Error { constructor( message: string, options: ErrorOptions, public readonly mail: Mail, ) { super(message, options); } } type MailDeliverHook = (mail: Mail) => Promise; export abstract class MailDeliverer { preHooks: MailDeliverHook[] = []; postHooks: MailDeliverHook[] = []; constructor(public readonly destination: string) {} protected doPrepare(_mail: Mail): Promise { return Promise.resolve(); } protected abstract doDeliver(mail: Mail): Promise; protected doFinalize(_mail: Mail): Promise { return Promise.resolve(); } async deliverRaw(raw: string): Promise { const mail = new Mail(raw); await this.deliver(mail); } async deliver(mail: Mail): Promise { this.doPrepare(mail); for (const hook of this.preHooks) { await hook(mail); } await this.doDeliver(mail); for (const hook of this.postHooks) { await hook(mail); } await this.doFinalize(mail); } protected throwError( reason: string, mail: Mail, cause?: unknown, ): never { throw new MailDeliverError( `Failed to deliver mail to ${this.destination}: ${reason}`, { cause }, mail, ); } }