diff --git a/README.md b/README.md index 5b50613..5a95d25 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,11 @@ CallableKit provides typesafe rpc with Swift server # Example -- Define interface protocol and share this module for server and client. +Define interface protocol and share the module to server and client. +Interface protocols must be annotated with `@Callable` ```swift +@Callable public protocol EchoServiceProtocol { func hello(request: EchoHelloRequest) async throws -> EchoHelloResponse } @@ -31,11 +33,11 @@ public struct EchoHelloResponse: Codable, Sendable { } ``` -- Run code generation - -- Server implements thet protocol and register to routes. +Server implements thet protocol and register to routes. ```swift +import APIDefinition // Your interface module +import CallableKitVaporTransport import Vapor struct EchoService: EchoServiceProtocol { @@ -44,34 +46,41 @@ struct EchoService: EchoServiceProtocol { } } -let app = Application() -defer { app.shutdown() } -let echoProvider = EchoServiceProvider { _ in // EchoServiceProvider is generated type +let app = try await Application.make() + +// Swift macro generates `configureEchoServiceProtocol` +configureEchoServiceProtocol(transport: VaporTransport(router: app.routes) { _ in EchoService() -} -try app.register(collection: echoProvider) -try app.run() +}) + +try await app.execute() +try await app.asyncShutdown() ``` -- Client can call the functions through stub client. Using the same protocol. +Client can call the functions through stub client. Using the same protocol. ```swift -let client: some StubClientProtocol = FoundationHTTPStubClient( - baseURL: URL(string: "http://127.0.0.1:8080")!, +import APIDefinition +import CallableKitURLSessionStub + +let stubClient: some StubClientProtocol = URLSessionStubClient( + baseURL: URL(string: "http://127.0.0.1:8080")! ) -// .echo.hello is generated extension -let res = try await client.echo.hello(request: .init(name: "Swift")) +// EchoServiceProtocolStub is also generated by Swift macro +let echoClient = EchoServiceProtocolStub(client: stubClient) +let res = try await echoClient.hello(request: .init(name: "Swift")) print(res.message) // Hello, Swift! ``` -- TypeScript client also supported! +TypeScript client is also supported. +It needs manual code generation. + ```ts const stub = createStubClient("http://127.0.0.1:8080"); const echoClient = bindEcho(stub); -const res = await echoClient.hello({ name: "TypeScript" }); +const res/*: { message: string }*/ = await echoClient.hello({ name: "TypeScript" }); console.log(res.message); // Hello, TypeScript! -// ^? res: { message: string } ``` Swift types are coverted to TS types powered by [CodableToTypeScript](https://github.com/omochi/CodableToTypeScript) @@ -82,23 +91,21 @@ Swift types are coverted to TS types powered by [CodableToTypeScript](https://gi ```sh $ swift run codegen Sources/APIDefinition \ - --client_out Sources/Client/Gen \ - --vapor_out Sources/Server/Gen \ --ts_out TSClient/src/Gen \ ``` [Mint](https://github.com/yonaskolb/Mint) is useful to checkout and run. -or +or - Use from package plugin (see [example](https://github.com/sidepelican/CallableKit/tree/main/example)) - + Add plugin target in your Package.swift (or add dedicated Package.swift for independency) ```swift dependencies: [ ... - .package(url: "https://github.com/sidepelican/CallableKit", from: "1.0.0"), + .package(url: "https://github.com/sidepelican/CallableKit.git", from: "1.0.0"), ], targets: [ ... diff --git a/Sources/CallableKit/CallableKitEmpty.swift b/Sources/CallableKit/CallableKitEmpty.swift index 804917e..7a2a077 100644 --- a/Sources/CallableKit/CallableKitEmpty.swift +++ b/Sources/CallableKit/CallableKitEmpty.swift @@ -1,3 +1,3 @@ -@usableFromInline internal struct CallableKitEmpty: Codable { +@usableFromInline internal struct CallableKitEmpty: Codable, Sendable { @usableFromInline init() {} } diff --git a/Sources/CallableKit/ServiceTransport.swift b/Sources/CallableKit/ServiceTransport.swift index 34e67f9..2916a0e 100644 --- a/Sources/CallableKit/ServiceTransport.swift +++ b/Sources/CallableKit/ServiceTransport.swift @@ -1,12 +1,12 @@ public protocol ServiceTransport { associatedtype Service - func register( + func register( path: String, methodSelector: @escaping @Sendable (Service.Type) -> (Service) -> (Request) async throws -> Void ) - func register( + func register( path: String, methodSelector: @escaping @Sendable (Service.Type) -> (Service) -> () async throws -> Response ) @@ -16,14 +16,14 @@ public protocol ServiceTransport { methodSelector: @escaping @Sendable (Service.Type) -> (Service) -> () async throws -> Void ) - func register( + func register( path: String, methodSelector: @escaping @Sendable (Service.Type) -> (Service) -> (Request) async throws -> Response ) } extension ServiceTransport { - @inlinable public func register( + @inlinable public func register( path: String, methodSelector: @escaping @Sendable (Service.Type) -> (Service) -> (Request) async throws -> Void ) { @@ -37,7 +37,7 @@ extension ServiceTransport { } } - @inlinable public func register( + @inlinable public func register( path: String, methodSelector: @escaping @Sendable (Service.Type) -> (Service) -> () async throws -> Response ) { diff --git a/example/Sources/Client/Main.swift b/example/Sources/Client/ClientMain.swift similarity index 99% rename from example/Sources/Client/Main.swift rename to example/Sources/Client/ClientMain.swift index f628c0d..baeb53c 100644 --- a/example/Sources/Client/Main.swift +++ b/example/Sources/Client/ClientMain.swift @@ -10,7 +10,7 @@ struct ErrorFrame: Decodable, CustomStringConvertible, LocalizedError { var errorDescription: String? { description } } -@main struct Main { +@main struct ClientMain { static func main() async throws { let client: some StubClientProtocol = URLSessionStubClient( baseURL: URL(string: "http://127.0.0.1:8080")!, diff --git a/example/Sources/HBServer/Main.swift b/example/Sources/HBServer/HBMain.swift similarity index 98% rename from example/Sources/HBServer/Main.swift rename to example/Sources/HBServer/HBMain.swift index 45b5a53..89d7982 100644 --- a/example/Sources/HBServer/Main.swift +++ b/example/Sources/HBServer/HBMain.swift @@ -23,7 +23,7 @@ struct ErrorMiddleware: MiddlewareProtocol { } } -@main struct Main { +@main struct HBMain { static func main() async throws { let router = Router() router.add(middleware: ErrorMiddleware()) diff --git a/example/Sources/VaporServer/main.swift b/example/Sources/VaporServer/VaporMain.swift similarity index 98% rename from example/Sources/VaporServer/main.swift rename to example/Sources/VaporServer/VaporMain.swift index 4743e39..fd0ab0d 100644 --- a/example/Sources/VaporServer/main.swift +++ b/example/Sources/VaporServer/VaporMain.swift @@ -3,7 +3,7 @@ import CallableKitVaporTransport import Vapor import Service -@main struct Main { +@main struct VaporMain { static func main() async throws { let app = try await Application.make()