From 95a9371233346f4e65a3a29538a3cbb9bf976cf3 Mon Sep 17 00:00:00 2001 From: Sammy Smallman Date: Mon, 14 Aug 2023 19:50:10 +0100 Subject: [PATCH] feat: Flatten OSCBundles (Issue-#8) Signed-off-by: Sam Smallman --- Sources/CoreOSC/CoreOSC.swift | 2 +- Sources/CoreOSC/OSCBundle.swift | 14 ++++ Sources/CoreOSC/OSCMessage.swift | 10 ++- Sources/CoreOSC/en.lproj/Localizable.strings | 3 + Tests/CoreOSCTests/CoreOSCTests.swift | 3 +- Tests/CoreOSCTests/OSCBundleTests.swift | 82 ++++++++++++++++++++ 6 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 Tests/CoreOSCTests/OSCBundleTests.swift diff --git a/Sources/CoreOSC/CoreOSC.swift b/Sources/CoreOSC/CoreOSC.swift index 0f82338..21a0b33 100644 --- a/Sources/CoreOSC/CoreOSC.swift +++ b/Sources/CoreOSC/CoreOSC.swift @@ -26,7 +26,7 @@ import Foundation public enum CoreOSC { /// This package's semantic version number, mirrored also in git history as a `git tag`. - public static let version: String = "1.2.1" + public static let version: String = "1.3.0" /// The license agreement this repository is licensed under. public static let license: String = { diff --git a/Sources/CoreOSC/OSCBundle.swift b/Sources/CoreOSC/OSCBundle.swift index ac7d9b2..d9d1b48 100644 --- a/Sources/CoreOSC/OSCBundle.swift +++ b/Sources/CoreOSC/OSCBundle.swift @@ -66,4 +66,18 @@ public struct OSCBundle: OSCPacket { return result } + /// Flatten the elements contained by the bundle, ignoring all `OSCTimeTag`'s. + /// - Returns: An array of `OSCMessage`'s. + public func flatten() -> [OSCMessage] { + elements.reduce([OSCMessage]()) { + if let message = $1 as? OSCMessage { + return $0 + [message] + } else if let bundle = $1 as? OSCBundle { + return $0 + bundle.flatten() + } else { + return $0 + } + } + } + } diff --git a/Sources/CoreOSC/OSCMessage.swift b/Sources/CoreOSC/OSCMessage.swift index c8c77c8..a774887 100644 --- a/Sources/CoreOSC/OSCMessage.swift +++ b/Sources/CoreOSC/OSCMessage.swift @@ -24,8 +24,14 @@ import Foundation /// An OSC Message. -public struct OSCMessage: OSCPacket { - +public struct OSCMessage: OSCPacket, Equatable { + + public static func == (lhs: OSCMessage, rhs: OSCMessage) -> Bool { + lhs.addressPattern == rhs.addressPattern && + lhs.typeTagString == rhs.typeTagString && + lhs.arguments.map { $0.oscData } == rhs.arguments.map { $0.oscData } + } + /// The OSC Address Pattern associated with the message that represents the full path to one /// or more OSC Methods through pattern matching. public private(set) var addressPattern: OSCAddressPattern diff --git a/Sources/CoreOSC/en.lproj/Localizable.strings b/Sources/CoreOSC/en.lproj/Localizable.strings index ff8c017..915aa1a 100644 --- a/Sources/CoreOSC/en.lproj/Localizable.strings +++ b/Sources/CoreOSC/en.lproj/Localizable.strings @@ -1,5 +1,8 @@ /* OSC Address Error */ +/* OSC Version */ +"OSC_VERSION" = "1.3.0"; + /* OSC Address Error: Invalid Address */ "OSC_ADDRESS_ERROR_INVALID_ADDRESS" = "Invalid address"; diff --git a/Tests/CoreOSCTests/CoreOSCTests.swift b/Tests/CoreOSCTests/CoreOSCTests.swift index 763c787..7155ec5 100644 --- a/Tests/CoreOSCTests/CoreOSCTests.swift +++ b/Tests/CoreOSCTests/CoreOSCTests.swift @@ -29,7 +29,8 @@ import XCTest class CoreOSCTests: XCTestCase { func testVersion() { - XCTAssertEqual(CoreOSC.version, "1.2.1") + XCTAssertEqual(CoreOSC.version, "1.3.0") + XCTAssertEqual(NSLocalizedString("OSC_VERSION", bundle: .module, comment: "OSC Version"), CoreOSC.version) } func testLicense() { diff --git a/Tests/CoreOSCTests/OSCBundleTests.swift b/Tests/CoreOSCTests/OSCBundleTests.swift new file mode 100644 index 0000000..eff9ae3 --- /dev/null +++ b/Tests/CoreOSCTests/OSCBundleTests.swift @@ -0,0 +1,82 @@ +// +// OSCBundleTests.swift +// CoreOSCTests +// +// Created by Sam Smallman on 14/08/2023. +// Copyright © 2023 Sam Smallman. https://github.com/SammySmallman +// +// This file is part of CoreOSC +// +// CoreOSC is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// CoreOSC is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +import XCTest + +@testable import CoreOSC + +final class OSCABundleTests: XCTestCase { + + func testSimpleFlatten() throws { + let messages = [ + try! OSCMessage(with: "/core/osc/1"), + try! OSCMessage(with: "/core/osc/2"), + try! OSCMessage(with: "/core/osc/3") + ] + let bundle = OSCBundle( + messages, + timeTag: .immediate + ) + XCTAssertEqual(bundle.flatten(), messages) + } + + func testRecursiveFlatten() throws { + let bundle = OSCBundle( + [ + try! OSCMessage(with: "/core/osc/1"), + try! OSCMessage(with: "/core/osc/2"), + try! OSCMessage(with: "/core/osc/3"), + OSCBundle( + [ + try! OSCMessage(with: "/core/osc/4"), + try! OSCMessage(with: "/core/osc/5"), + try! OSCMessage(with: "/core/osc/6"), + OSCBundle( + [ + try! OSCMessage(with: "/core/osc/7"), + try! OSCMessage(with: "/core/osc/8"), + try! OSCMessage(with: "/core/osc/9"), + + ], + timeTag: .immediate + ) + ], + timeTag: .immediate + ) + ], + timeTag: .immediate + ) + XCTAssertEqual(bundle.flatten(), [ + try! OSCMessage(with: "/core/osc/1"), + try! OSCMessage(with: "/core/osc/2"), + try! OSCMessage(with: "/core/osc/3"), + try! OSCMessage(with: "/core/osc/4"), + try! OSCMessage(with: "/core/osc/5"), + try! OSCMessage(with: "/core/osc/6"), + try! OSCMessage(with: "/core/osc/7"), + try! OSCMessage(with: "/core/osc/8"), + try! OSCMessage(with: "/core/osc/9") + ]) + } + +}