diff --git a/Sources/DBus/BusName.swift b/Sources/DBus/BusName.swift index dc8d336..5fdbb5a 100644 --- a/Sources/DBus/BusName.swift +++ b/Sources/DBus/BusName.swift @@ -59,9 +59,9 @@ internal extension DBusBusName { static func validate(_ string: String) throws { - let error = DBusError.Reference() + let error = DBusError() guard Bool(dbus_validate_bus_name(string, &error.internalValue)) - else { throw DBusError(error)! } + else { throw error } } } diff --git a/Sources/DBus/Connection.swift b/Sources/DBus/Connection.swift index a80c900..7c278f8 100644 --- a/Sources/DBus/Connection.swift +++ b/Sources/DBus/Connection.swift @@ -53,7 +53,7 @@ public final class DBusConnection { self.shared = shared - let error = DBusError.Reference() + let error = DBusError() if shared { @@ -65,7 +65,7 @@ public final class DBusConnection { } // check for error - if let error = DBusError(error) { + if error.isSet { throw error } @@ -80,7 +80,7 @@ public final class DBusConnection { self.shared = shared - let error = DBusError.Reference() + let error = DBusError() let internalBusType = CDBus.DBusBusType(rawValue: busType.rawValue) @@ -94,7 +94,7 @@ public final class DBusConnection { } // check for error - if let error = DBusError(error) { + if error.isSet { throw error } @@ -172,7 +172,7 @@ public final class DBusConnection { var serialNumber: dbus_uint32_t = 0 guard Bool(dbus_connection_send(internalPointer, message.internalPointer, &serialNumber)) - else { throw DBusError(name: .failed, message: "") } + else { throw try DBusError(name: DBusError.Name.failed, message: "") } return serialNumber } @@ -190,7 +190,7 @@ public final class DBusConnection { } guard Bool(dbus_connection_send_with_reply(internalPointer, message.internalPointer, pendingCallDoublePointer, timeout.rawValue)) - else { throw DBusError(name: .failed, message: "No memory") } + else { throw try DBusError(name: DBusError.Name.failed, message: "No memory") } // if the connection is disconnected or you try to send Unix file descriptors on a connection that does not support them, // the DBusPendingCall will be set to NULL diff --git a/Sources/DBus/Error.swift b/Sources/DBus/Error.swift index 2ced99c..8d2fd71 100644 --- a/Sources/DBus/Error.swift +++ b/Sources/DBus/Error.swift @@ -9,192 +9,115 @@ import Foundation import CDBus -/// DBus type representing an exception. -public struct DBusError: Error, Equatable, Hashable { - - /// Error name field - public let name: DBusError.Name - - /// Error message field - public let message: String - - internal init(name: DBusError.Name, message: String) { - - self.name = name - self.message = message - } +// This error is for DBus (swift) runtime failures. +public enum RuntimeError: Error { + case generic(String) } -// MARK: - Internal Reference - -internal extension DBusError { - - /// Internal class for working with the C DBus error API - internal final class Reference { - - // MARK: - Internal Properties - - internal var internalValue: CDBus.DBusError - - // MARK: - Initialization - - deinit { - - dbus_error_free(&internalValue) - } - - /// Creates New DBus Error instance. - init() { - - var internalValue = CDBus.DBusError() - dbus_error_init(&internalValue) - self.internalValue = internalValue - } - - // MARK: - Properties - - /// Checks whether an error occurred (the error is set). - /// - /// - Returns: `true` if the error is empty or `false` if the error is set. - var isEmpty: Bool { - - return Bool(dbus_error_is_set(&internalValue)) == false - } - - var name: String { - - return String(cString: internalValue.name) - } - - var message: String { - - return String(cString: internalValue.message) - } - - func hasName(_ name: String) -> Bool { - - return Bool(dbus_error_has_name(&internalValue, name)) +// Given a swift string, make a copy of the C string (const char *) and return a pointer to it. +// This will throw RuntimeError.generic if malloc() fails∫. +func swiftStringToConstCharStar(_ s: String) throws -> UnsafePointer { + return try s.withCString { (unsafePointer: UnsafePointer) -> UnsafePointer in + // We need to copy the string to save a copy. unsafePointer is only valid in this closure + // UnsafeMutableRawPointer + let bufferLen = strlen(unsafePointer) + 1 + guard let unsafeMutableRawPointer = malloc(bufferLen) else { + throw RuntimeError.generic("malloc() failed") } + memcpy(unsafeMutableRawPointer, unsafePointer, bufferLen) + // UnsafeMutablePointer + let unsafeMutablePointer = unsafeMutableRawPointer.assumingMemoryBound(to: Int8.self) + // UnsafePointer + return UnsafePointer(unsafeMutablePointer) } } -internal extension DBusError { - - init?(_ reference: DBusError.Reference) { - - guard reference.isEmpty == false - else { return nil } - - guard let name = DBusError.Name(rawValue: reference.name) - else { fatalError("Invalid error \(reference.name)") } - - self.init(name: name, - message: reference.message) +// This is a wrapper of the libdbus DBusError type. +public class DBusError: Error, Equatable, CustomStringConvertible { + internal var internalValue = CDBus.DBusError() + + init() { + dbus_error_init(&internalValue); } -} -// MARK: CustomNSError + // This will throw RuntimeError.generic if the name passed in is not a valid DBus error name, + // or if malloc() fails when we are copying the strings to live in C land. + convenience init(name: String, message: String = "") throws { + self.init() -extension DBusError: CustomNSError { - - public enum UserInfoKey: String { - - /// DBus error name - case name + let validationError = DBusError() + let isValid = dbus_validate_error_name(name, &validationError.internalValue) + if isValid == false { + throw RuntimeError.generic("\(name) is not a valid DBus Error name.") + } + + let cName = try swiftStringToConstCharStar(name) + let cMessage = try swiftStringToConstCharStar(message) + dbus_set_error_const(&internalValue, cName, cMessage) } - - /// The domain of the error. - public static let errorDomain = "org.freedesktop.DBus.Error" - - /// The error code within the given domain. - public var errorCode: Int { - - return hashValue + + deinit { + dbus_error_free(&internalValue) } - - /// The user-info dictionary. - public var errorUserInfo: [String: Any] { - - return [ - UserInfoKey.name.rawValue: self.name.rawValue, - NSLocalizedDescriptionKey: self.message - ] + + public var isSet: Bool { + let dbusBool = dbus_error_is_set(&internalValue) + return Bool(dbusBool) } -} -// MARK: Error Name + public var name: String { + return String(cString: internalValue.name) + } -public extension DBusError { - - public struct Name: Equatable, Hashable { - - public let rawValue: String - - public init?(rawValue: String) { - - // validate from C, no parsing - do { try DBusInterface.validate(rawValue) } - catch { return nil } - - self.rawValue = rawValue - } + public var message: String { + return String(cString: internalValue.message) } -} -public extension DBusError.Name { - - public init(_ interface: DBusInterface) { - - // should be valid - self.rawValue = interface.rawValue + public static func == (lhs: DBusError, rhs: DBusError) -> Bool { + let lhsName = String(cString: lhs.internalValue.name) + let rhsName = String(cString: rhs.internalValue.name) + let lhsMessage = String(cString: lhs.internalValue.message) + let rhsMessage = String(cString: rhs.internalValue.message) + return (lhsName == rhsName && + lhsMessage == rhsMessage) } -} -public extension DBusInterface { - - init(_ error: DBusError.Name) { - - self.init(rawValue: error.rawValue)! + public var description: String { + return "DBusError(name: '\(name)', message: '\(message)') " } } -public extension DBusError.Name { - - /// A generic error; "something went wrong" - see the error message for more. - /// - /// `org.freedesktop.DBus.Error.Failed` - public static let failed = DBusError.Name(rawValue: DBUS_ERROR_FAILED)! - - /// No Memory - /// - /// `org.freedesktop.DBus.Error.NoMemory` - public static let noMemory = DBusError.Name(rawValue: DBUS_ERROR_NO_MEMORY)! - - /// Existing file and the operation you're using does not silently overwrite. - /// - /// `org.freedesktop.DBus.Error.FileExists` - public static let fileExists = DBusError.Name(rawValue: DBUS_ERROR_FILE_EXISTS)! - - /// Missing file. - /// - /// `org.freedesktop.DBus.Error.FileNotFound` - public static let fileNotFound = DBusError.Name(rawValue: DBUS_ERROR_FILE_NOT_FOUND)! - - /// Invalid arguments - /// - /// `org.freedesktop.DBus.Error.InvalidArgs` - public static let invalidArguments = DBusError.Name(rawValue: DBUS_ERROR_INVALID_ARGS)! - - /// Invalid signature - /// - /// `org.freedesktop.DBus.Error.InvalidSignature` - public static let invalidSignature = DBusError.Name(rawValue: DBUS_ERROR_INVALID_SIGNATURE)! -} +public extension DBusError { -extension DBusError.Name: CustomStringConvertible { - - public var description: String { - - return rawValue + public struct Name { + /// A generic error; "something went wrong" - see the error message for more. + /// + /// `org.freedesktop.DBus.Error.Failed` + public static let failed = String(DBUS_ERROR_FAILED) + + /// No Memory + /// + /// `org.freedesktop.DBus.Error.NoMemory` + public static let noMemory = String(DBUS_ERROR_NO_MEMORY) + + /// Existing file and the operation you're using does not silently overwrite. + /// + /// `org.freedesktop.DBus.Error.FileExists` + public static let fileExists = String(DBUS_ERROR_FILE_EXISTS) + + /// Missing file. + /// + /// `org.freedesktop.DBus.Error.FileNotFound` + public static let fileNotFound = String(DBUS_ERROR_FILE_NOT_FOUND) + + /// Invalid arguments + /// + /// `org.freedesktop.DBus.Error.InvalidArgs` + public static let invalidArguments = String(DBUS_ERROR_INVALID_ARGS) + + /// Invalid signature + /// + /// `org.freedesktop.DBus.Error.InvalidSignature` + public static let invalidSignature = String(DBUS_ERROR_INVALID_SIGNATURE) } } diff --git a/Sources/DBus/Interface.swift b/Sources/DBus/Interface.swift index deb0df7..537937e 100644 --- a/Sources/DBus/Interface.swift +++ b/Sources/DBus/Interface.swift @@ -100,9 +100,9 @@ internal extension DBusInterface { static func validate(_ string: String) throws { - let error = DBusError.Reference() + let error = DBusError() guard Bool(dbus_validate_interface(string, &error.internalValue)) - else { throw DBusError(error)! } + else { throw error } } } diff --git a/Sources/DBus/Member.swift b/Sources/DBus/Member.swift index d1cfe43..3b74875 100644 --- a/Sources/DBus/Member.swift +++ b/Sources/DBus/Member.swift @@ -35,9 +35,9 @@ internal extension DBusMember { static func validate(_ string: String) throws { - let error = DBusError.Reference() + let error = DBusError() guard Bool(dbus_validate_member(string, &error.internalValue)) - else { throw DBusError(error)! } + else { throw error } } } diff --git a/Sources/DBus/Message.swift b/Sources/DBus/Message.swift index de26075..dd0bdbc 100644 --- a/Sources/DBus/Message.swift +++ b/Sources/DBus/Message.swift @@ -38,7 +38,7 @@ public final class DBusMessage { public init(type: DBusMessageType) throws { guard let internalPointer = dbus_message_new(type.rawValue) - else { throw DBusError.messageInitializationOutOfMemory } + else { throw RuntimeError.generic("dbus_message_new() failed") } self.internalPointer = internalPointer } @@ -52,8 +52,8 @@ public final class DBusMessage { /// - Parameter error: A tuple consisting of the message to reply to, the error name, and the error message. public init(error: Error) throws { - guard let internalPointer = dbus_message_new_error(error.replyTo.internalPointer, error.name.rawValue, error.message) - else { throw DBusError.messageInitializationOutOfMemory } + guard let internalPointer = dbus_message_new_error(error.replyTo.internalPointer, error.name, error.message) + else { throw RuntimeError.generic("dbus_message_new_error() failed") } self.internalPointer = internalPointer } @@ -65,7 +65,7 @@ public final class DBusMessage { // Returns NULL if memory can't be allocated for the message. guard let internalPointer = dbus_message_new_method_call(methodCall.destination?.rawValue, methodCall.path.rawValue, methodCall.interface?.rawValue, methodCall.method) else { - throw DBusError.messageInitializationOutOfMemory + throw RuntimeError.generic("dbus_message_new_method_call() failed") } self.internalPointer = internalPointer @@ -75,7 +75,7 @@ public final class DBusMessage { public init(methodReturn: DBusMessage) throws { guard let internalPointer = dbus_message_new_method_return(methodReturn.internalPointer) - else { throw DBusError.messageInitializationOutOfMemory } + else { throw RuntimeError.generic("dbus_message_new_method_return() failed") } self.internalPointer = internalPointer } @@ -88,7 +88,7 @@ public final class DBusMessage { public init(signal: Signal) throws { guard let internalPointer = dbus_message_new_signal(signal.path, signal.interface, signal.name) - else { throw DBusError.messageInitializationOutOfMemory } + else { throw RuntimeError.generic("dbus_message_new_signal() failed") } self.internalPointer = internalPointer } @@ -153,7 +153,7 @@ public final class DBusMessage { public func setReplySerial(_ newValue: UInt32) throws { guard Bool(dbus_message_set_reply_serial(internalPointer, newValue)) - else { throw DBusError.messageSetValueOutOfMemory } + else { throw RuntimeError.generic("dbus_message_set_reply_serial() failed") } } /// Flag indicating that the caller of the method is prepared to wait for interactive authorization to take place @@ -219,23 +219,20 @@ public final class DBusMessage { /// /// The name is fully-qualified (namespaced). /// The error name must contain only valid characters as defined in the D-Bus specification. - public var errorName: DBusError.Name? { + public var errorName: String? { - guard let string = getString(dbus_message_get_error_name) + guard let name = getString(dbus_message_get_error_name) else { return nil } - - guard let name = DBusError.Name(rawValue: string) - else { fatalError("Invalid error name \(string)") } - + return name } /// Sets the name of the error (DBUS_MESSAGE_TYPE_ERROR). /// The name is fully-qualified (namespaced). /// The error name must contain only valid characters as defined in the D-Bus specification. - public func setErrorName(_ newValue: DBusError.Name?) throws { + public func setErrorName(_ newValue: String) throws { - try setString(dbus_message_set_error_name, newValue?.rawValue) + try setString(dbus_message_set_error_name, newValue) } /// The interface this message is being sent to (for method call type) @@ -348,12 +345,12 @@ public final class DBusMessage { if let newValue = newValue { guard Bool(newValue.withCString({ function(internalPointer, $0) })) - else { throw DBusError.messageSetValueOutOfMemory } + else { throw RuntimeError.generic("DBusMessage.setString() failed") } } else { guard Bool(function(internalPointer, nil)) - else { throw DBusError.messageSetValueOutOfMemory } + else { throw RuntimeError.generic("DBusMessage.setString() failed") } } } } @@ -368,7 +365,7 @@ public extension DBusMessage { public func copy() throws -> DBusMessage { guard let copyPointer = dbus_message_copy(internalPointer) - else { throw DBusError(name: .noMemory, message: "Could not copy message") } + else { throw try DBusError(name: DBusError.Name.noMemory, message: "Could not copy message") } let copyMessage = DBusMessage(copyPointer) @@ -419,10 +416,10 @@ public extension DBusMessage { public struct Error { public let replyTo: DBusMessage - public let name: DBusError.Name + public let name: String public let message: String - public init(replyTo: DBusMessage, name: DBusError.Name, message: String) { + public init(replyTo: DBusMessage, name: String, message: String) { self.replyTo = replyTo self.name = name @@ -458,57 +455,20 @@ public extension DBusMessage { } } -internal extension DBusError.Reference { - +public extension DBusError { /** Sets a DBusError based on the contents of the given message. - + The error is only set if the message is an error message, as in `DBusMessageType.error`. The name of the error is set to the name of the message, and the error message is set to the first argument if the argument exists and is a string. - */ + */ convenience init?(message: DBusMessage) { - guard message.type == .error else { return nil } - + self.init() guard Bool(dbus_set_error_from_message(&self.internalValue, message.internalPointer)) else { return nil } - - assert(isEmpty == false) - } -} -public extension DBusError { - - /** - Sets a DBusError based on the contents of the given message. - - The error is only set if the message is an error message, as in `DBusMessageType.error`. The name of the error is set to the name of the message, and the error message is set to the first argument if the argument exists and is a string. - */ - init?(message: DBusMessage) { - - guard let reference = Reference(message: message) - else { return nil } - - self.init(reference) - } -} - -// MARK: - Private Extensions - -private extension DBusError { - - // Could not initialize message due to lack of memory. - static var messageInitializationOutOfMemory: DBusError { - - return DBusError(name: .noMemory, message: "Could not initialize message due to lack of memory.") - } - - // Could not modify message due to lack of memory. - static var messageSetValueOutOfMemory: DBusError { - - // If this fails due to lack of memory, the message is hosed and you have to start over building the whole message. - // FALSE if not enough memory - return DBusError(name: .noMemory, message: "Could not modify message due to lack of memory.") + assert(isSet == true) } } diff --git a/Sources/DBus/MessageIterator.swift b/Sources/DBus/MessageIterator.swift index 69f1db3..8e83f79 100644 --- a/Sources/DBus/MessageIterator.swift +++ b/Sources/DBus/MessageIterator.swift @@ -131,7 +131,7 @@ extension DBusMessageIter { private mutating func signature() throws -> DBusSignature { guard let cString = dbus_message_iter_get_signature(&self) - else { throw DBusError(name: .noMemory, message: "Could not get signature") } + else { throw try DBusError(name: DBusError.Name.noMemory, message: "Could not get signature") } let string = String(cString: cString) @@ -242,7 +242,7 @@ internal extension DBusMessageIter { guard withUnsafePointer(to: &basicValue, { Bool(dbus_message_iter_append_basic(&self, Int32(type.integerValue), UnsafeRawPointer($0))) - }) else { throw DBusError.messageAppendOutOfMemory } + }) else { throw RuntimeError.generic("dbus_message_iter_append_basic() failed") } } private mutating func append(_ string: String, _ type: DBusType = .string) throws { @@ -266,21 +266,10 @@ internal extension DBusMessageIter { */ guard Bool(dbus_message_iter_open_container(&self, Int32(type.integerValue), signature?.rawValue, &subIterator)) - else { throw DBusError.messageAppendOutOfMemory } + else { throw RuntimeError.generic("dbus_message_iter_open_container() failed") } defer { dbus_message_iter_close_container(&self, &subIterator) } try container(&subIterator) } } - -private extension DBusError { - - // Argument could not be appended due to lack of memory. - static var messageAppendOutOfMemory: DBusError { - - // If this fails due to lack of memory, the message is hosed and you have to start over building the whole message. - // FALSE if not enough memory - return DBusError(name: .noMemory, message: "Argument could not be appended to message due to lack of memory.") - } -} diff --git a/Sources/DBus/ObjectPath.swift b/Sources/DBus/ObjectPath.swift index 95ee3e4..068c788 100644 --- a/Sources/DBus/ObjectPath.swift +++ b/Sources/DBus/ObjectPath.swift @@ -94,9 +94,9 @@ internal extension DBusObjectPath { static func validate(_ string: String) throws { - let error = DBusError.Reference() + let error = DBusError() guard Bool(dbus_validate_path(string, &error.internalValue)) - else { throw DBusError(error)! } + else { throw error } } } diff --git a/Sources/DBus/Signature.swift b/Sources/DBus/Signature.swift index 329629a..f40f5c1 100644 --- a/Sources/DBus/Signature.swift +++ b/Sources/DBus/Signature.swift @@ -49,9 +49,9 @@ internal extension DBusSignature { static func validate(_ string: String) throws { - let error = DBusError.Reference() + let error = DBusError() guard Bool(dbus_signature_validate(string, &error.internalValue)) - else { throw DBusError(error)! } + else { throw error } } diff --git a/Tests/DBusTests/ErrorTests.swift b/Tests/DBusTests/ErrorTests.swift new file mode 100644 index 0000000..c38cddf --- /dev/null +++ b/Tests/DBusTests/ErrorTests.swift @@ -0,0 +1,36 @@ +// +// ErrorTests.swift +// DBus +// +// Created by Tabor Kelly on 1/28/19. +// Copyright © 2019 PureSwift. All rights reserved. +// + +import Foundation +import XCTest +@testable import DBus + +final class ErrorTests: XCTestCase { + + static let allTests = [ + ("testNewGoodError", testNewGoodError), + ("testBadErrorThrows", testBadErrorThrows), + ] + + func testNewGoodError() { + do { + let name = "org.freedesktop.DBus.Error.InvalidArgs" + let message = "Foo!" + let e = try DBusError(name: name, message: message) + XCTAssertEqual(name, e.name) + XCTAssertEqual(message, e.message) + // let r = e.Reference() + } catch { + XCTFail("\(error)") + } + } + + func testBadErrorThrows() { + XCTAssertThrowsError(try DBusError(name: ".foo", message: "nobody loves buggy code")) + } +} diff --git a/Tests/DBusTests/InterfaceTests.swift b/Tests/DBusTests/InterfaceTests.swift index 55fc4d4..ba84295 100644 --- a/Tests/DBusTests/InterfaceTests.swift +++ b/Tests/DBusTests/InterfaceTests.swift @@ -43,7 +43,7 @@ final class InterfaceTests: XCTestCase { XCTAssertThrowsError(try DBusInterface.validate(string)) do { try DBusInterface.validate(string) } catch let error as DBusError { - XCTAssertEqual(error.name, .invalidArguments) + XCTAssertEqual(error.name, DBusError.Name.invalidArguments) print("\"\(string)\" is invalid: \(error.message)") return } diff --git a/Tests/DBusTests/MessageTests.swift b/Tests/DBusTests/MessageTests.swift index 4525eae..3d29bfd 100644 --- a/Tests/DBusTests/MessageTests.swift +++ b/Tests/DBusTests/MessageTests.swift @@ -145,7 +145,7 @@ final class MessageTests: XCTestCase { originalMessage.serial = 1 // fake it till you make it #endif - let error = DBusError(name: .failed, message: "Test Error") + let error = try DBusError(name: DBusError.Name.failed, message: "Test Error") let errorMessage = try DBusMessage(error: DBusMessage.Error(replyTo: originalMessage, error: error)) diff --git a/Tests/DBusTests/ObjectPathTests.swift b/Tests/DBusTests/ObjectPathTests.swift index 4b6457c..a6081cd 100644 --- a/Tests/DBusTests/ObjectPathTests.swift +++ b/Tests/DBusTests/ObjectPathTests.swift @@ -40,7 +40,7 @@ final class ObjectPathTests: XCTestCase { XCTAssertThrowsError(try DBusObjectPath.validate(string)) do { try DBusObjectPath.validate(string) } catch let error as DBusError { - XCTAssertEqual(error.name, .invalidArguments) + XCTAssertEqual(error.name, DBusError.Name.invalidArguments) print("\"\(string)\" is invalid: \(error.message)"); return } catch { XCTFail("\(error)"); return } diff --git a/Tests/DBusTests/SignatureTests.swift b/Tests/DBusTests/SignatureTests.swift index ab0d26c..6d3818d 100644 --- a/Tests/DBusTests/SignatureTests.swift +++ b/Tests/DBusTests/SignatureTests.swift @@ -40,7 +40,7 @@ final class SignatureTests: XCTestCase { XCTAssertThrowsError(try DBusSignature.validate(string)) do { try DBusSignature.validate(string) } catch let error as DBusError { - XCTAssertEqual(error.name, .invalidSignature) + XCTAssertEqual(error.name, DBusError.Name.invalidSignature) print("\"\(string)\" is invalid: \(error.message)"); return } catch { XCTFail("Invalid error \(error)"); return }