Skip to content

Commit

Permalink
Add ouisync-service ffi bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
za-creature committed Jan 14, 2025
1 parent 900150d commit 478a2f4
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 2 deletions.
8 changes: 6 additions & 2 deletions bindings/swift/OuisyncLib/Plugins/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ PROJECT_HOME=$(realpath "$(dirname "$0")/../../../../")
export CARGO_HOME=$(realpath "$1")
export PATH="$CARGO_HOME/bin:$PATH"
export RUSTUP_HOME="$CARGO_HOME/.rustup"
export MACOSX_DEPLOYMENT_TARGET=13.0
BUILD_OUTPUT=$(realpath "$2")

# cargo builds some things that confuse xcode such as fingerprints and depfiles which cannot be
Expand Down Expand Up @@ -44,7 +45,7 @@ cd $PROJECT_HOME
for TARGET in ${(k)TARGETS}; do
cross build \
--frozen \
--package ouisync-ffi \
--package ouisync-service \
--target $TARGET \
--target-dir "$BUILD_OUTPUT" \
$FLAGS || fatal 1 "Unable to compile for $TARGET"
Expand All @@ -57,7 +58,10 @@ echo "module OuisyncLibFFI {
header \"bindings.h\"
export *
}" > "$INCLUDE/module.modulemap"
cbindgen --lang C --crate ouisync-ffi > "$INCLUDE/bindings.h" || fatal 2 "Unable to generate bindings.h"
cbindgen --lang C --crate ouisync-service > "$INCLUDE/bindings.h" || fatal 2 "Unable to generate bindings.h"
# hack for autoimporting enums https://stackoverflow.com/questions/60559599/swift-c-api-enum-in-swift
cp "$INCLUDE/bindings.h" "$BUILD_OUTPUT/bindings.h"
perl -i -p0e 's/enum\s+(\w+)([^}]+});\ntypedef (\w+) \1/typedef enum __attribute__\(\(enum_extensibility\(open\)\)\) : \3\2 \1/sg' "$INCLUDE/bindings.h"

# xcodebuild refuses multiple architectures per platform, instead expecting fat libraries when the
# destination operating system supports multiple architectures; apple also explicitly rejects any
Expand Down
65 changes: 65 additions & 0 deletions bindings/swift/OuisyncLib/Sources/Service.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import OuisyncLibFFI

extension ErrorCode: Swift.Error {}
public typealias Error = ErrorCode

public class Service {
// An opaque handle which must be passed to service_stop in order to terminate the service.
private var handle: UnsafeMutableRawPointer?;

/* Starts a Ouisync service in a new thread and binds it to the port set in `configDir`.
*
* Returns after the service has been initialized successfully and is ready to accept client
* connections, or throws a `Ouisync.Error` indicating what went wrong.
*
* On success, the service remains active until `.stop()` is called. An attempt will be made to
* stop the service once all references are dropped, however this is strongly discouraged as in
* this case it's not possible to determine whether the shutdown was successful. */
public init(configDir: String, debugLabel: String) async throws {
handle = try await withUnsafeThrowingContinuation {
service_start(configDir, debugLabel, Resume, unsafeBitCast($0, to: UnsafeRawPointer.self))
}
}

deinit {
guard let handle else { return }
service_stop(handle, Ignore, nil)
}

/* Stops a running Ouisync service.
*
* Returns after the service shutdown has been completed or throws a `Ouisync.Error` on failure.
* Returns immediately when called a second time, but doing so is not thread safe! */
public func stop() async throws {
guard let handle else { return }
self.handle = nil
try await withUnsafeThrowingContinuation {
service_stop(handle, Resume, unsafeBitCast($0, to: UnsafeRawPointer.self))
} as Void
}

/* Initialize logging to stdout. Should be called before `service_start`.
*
* If `filename` is not null, additionally logs to that file.
* If `handler` is not null, it is called for every message.
*
* Throws a `Ouisync.Error` on failure. Should not be called more than once per process!
*/
public static func configureLogging(filename: String? = nil,
handler: LogHandler? = nil,
tag: String = "Server") throws {
let err = log_init(filename, handler, tag)
if err != .Ok { throw err }
}
public typealias LogHandler = @convention(c) (LogLevel, UnsafePointer<CChar>?) -> Void
}

// ffi callbacks
fileprivate func Resume(context: UnsafeRawPointer?, error: Error) {
let continuation = unsafeBitCast(context, to: UnsafeContinuation<Void, any Swift.Error>.self)
switch error {
case .Ok: continuation.resume()
default: continuation.resume(throwing: error)
}
}
fileprivate func Ignore(context: UnsafeRawPointer?, error: Error) {}

0 comments on commit 478a2f4

Please sign in to comment.