How do I make this method more generic?

I seem to be my own worst enemy when it comes to truly understanding Swift’s various mechanisms for reducing boilerplate code. I feel like there's some elegant generic associated type-erased opaque protocol trickery to do this, but I couldn't figure it out.

Given

protocol Database {}
protocol Repository {}

protocol DatabaseRepository: Repository
{
    var database: Database { get }
    init(database: Database)
}

protocol TokenRepository : Repository
{
	func find(token: String, on: Database?) async throws -> Token?
}

struct DatabaseTokenRepository : TokenRepository, DatabaseRepository
{
	let database: Database
	
	func find(token inToken: String) async throws -> Token?
	{
		return try await Token
							.query(on: self.database)
							.filter(\.$token == inToken)
							.first()
	}
}

protocol Request
{
	var tokens	: TokenRepository { DatabaseTokenRepository(database: …) }
	var db		: Database
	var params	: [String:String]
}

I can write code like this:


private func tokenLogin(_ inReq: Request) async throws -> Response
{
	let code = try await inReq.tokens.find(code: inReq.param["token"])
	return Response(…)
}

But sometimes I need to do multiple things inside a transaction, and that's written like this (note that a transaction conforms to Database):

extension TokenRepository
{
	func on(db: Database) -> TokenRepository
	{
		return DatabaseTokenRepository(database: db)
	}
}

private func tokenLogin(_ inReq: Request) async throws -> Response
{
	try await inReq.db.transaction //	transaction conforms to Database
		{ txn: Database in
			let code = try await inReq.tokens.on(db: txn).find(code: inReq.param["token"])
		}
	
	return Response(…)
}

I’d like to write TokenRepository.on(db:) once, generically, and have it work for all Repository implementations. It would be acceptable to create an associated type Transaction which is a Database for Database implementations, and something else for other implementations (including a no-op). It would be okay to “pollute” those other implementations with the notion of a Transaction, even if they don’t support it.

Not sure if this is even possible.