@@ -108,7 +108,11 @@ internal final class DefaultLiveMap: LiveMap {
108108 // RTLM5c: If the channel is in the DETACHED or FAILED state, the library should indicate an error with code 90001
109109 let currentChannelState = coreSDK. channelState
110110 if currentChannelState == . detached || currentChannelState == . failed {
111- throw ARTErrorInfo . create ( withCode: Int ( ARTErrorCode . channelOperationFailedInvalidState. rawValue) , message: " LiveMap.get operation failed (invalid channel state: \( currentChannelState) ) " )
111+ throw LiveObjectsError . objectsOperationFailedInvalidChannelState (
112+ operationDescription: " LiveMap.get " ,
113+ channelState: currentChannelState,
114+ )
115+ . toARTErrorInfo ( )
112116 }
113117
114118 let entry = mutex. withLock {
@@ -120,78 +124,74 @@ internal final class DefaultLiveMap: LiveMap {
120124 return nil
121125 }
122126
123- // RTLM5d2: If a ObjectsMapEntry exists at the key
124-
125- // RTLM5d2a: If ObjectsMapEntry.tombstone is true, return undefined/null
126- if entry. tombstone == true {
127- return nil
128- }
129-
130- // Handle primitive values in the order specified by RTLM5d2b through RTLM5d2e
131-
132- // RTLM5d2b: If ObjectsMapEntry.data.boolean exists, return it
133- if let boolean = entry. data. boolean {
134- return . primitive( . bool( boolean) )
135- }
136-
137- // RTLM5d2c: If ObjectsMapEntry.data.bytes exists, return it
138- if let bytes = entry. data. bytes {
139- return . primitive( . data( bytes) )
140- }
127+ // RTLM5d2: If a ObjectsMapEntry exists at the key, convert it using the shared logic
128+ return convertEntryToLiveMapValue ( entry)
129+ }
141130
142- // RTLM5d2d: If ObjectsMapEntry.data.number exists, return it
143- if let number = entry. data. number {
144- return . primitive( . number( number. doubleValue) )
145- }
131+ internal var size : Int {
132+ get throws ( ARTErrorInfo) {
133+ // RTLM10c: If the channel is in the DETACHED or FAILED state, the library should throw an ErrorInfo error with statusCode 400 and code 90001
134+ let currentChannelState = coreSDK. channelState
135+ if currentChannelState == . detached || currentChannelState == . failed {
136+ throw LiveObjectsError . objectsOperationFailedInvalidChannelState (
137+ operationDescription: " LiveMap.size " ,
138+ channelState: currentChannelState,
139+ )
140+ . toARTErrorInfo ( )
141+ }
146142
147- // RTLM5d2e: If ObjectsMapEntry.data.string exists, return it
148- if let string = entry. data. string {
149- switch string {
150- case let . string( string) :
151- return . primitive( . string( string) )
152- case . json:
153- // TODO: Understand how to handle JSON values (https://github.com/ably/specification/pull/333/files#r2164561055)
154- notYetImplemented ( )
143+ return mutex. withLock {
144+ // RTLM10d: Returns the number of non-tombstoned entries (per RTLM14) in the internal data map
145+ mutableState. data. values. count { entry in
146+ // RTLM14a: The method returns true if ObjectsMapEntry.tombstone is true
147+ // RTLM14b: Otherwise, it returns false
148+ entry. tombstone != true
149+ }
155150 }
156151 }
152+ }
157153
158- // RTLM5d2f: If ObjectsMapEntry.data.objectId exists, get the object stored at that objectId from the internal ObjectsPool
159- if let objectId = entry. data. objectId {
160- // RTLM5d2f1: If an object with id objectId does not exist, return undefined/null
161- guard let poolEntry = delegate. referenced? . getObjectFromPool ( id: objectId) else {
162- return nil
154+ internal var entries : [ ( key: String , value: LiveMapValue ) ] {
155+ get throws ( ARTErrorInfo) {
156+ // RTLM11c: If the channel is in the DETACHED or FAILED state, the library should throw an ErrorInfo error with statusCode 400 and code 90001
157+ let currentChannelState = coreSDK. channelState
158+ if currentChannelState == . detached || currentChannelState == . failed {
159+ throw LiveObjectsError . objectsOperationFailedInvalidChannelState (
160+ operationDescription: " LiveMap.entries " ,
161+ channelState: currentChannelState,
162+ )
163+ . toARTErrorInfo ( )
163164 }
164165
165- // RTLM5d2f2: If an object with id objectId exists, return it
166- switch poolEntry {
167- case let . map( map) :
168- return . liveMap( map)
169- case let . counter( counter) :
170- return . liveCounter( counter)
171- }
172- }
166+ return mutex. withLock {
167+ // RTLM11d: Returns key-value pairs from the internal data map
168+ // RTLM11d1: Pairs with tombstoned entries (per RTLM14) are not returned
169+ var result : [ ( key: String , value: LiveMapValue ) ] = [ ]
173170
174- // RTLM5d2g: Otherwise, return undefined/null
175- return nil
176- }
171+ for (key, entry) in mutableState. data {
172+ // Convert entry to LiveMapValue using the same logic as get(key:)
173+ if let value = convertEntryToLiveMapValue ( entry) {
174+ result. append ( ( key: key, value: value) )
175+ }
176+ }
177177
178- internal var size : Int {
179- mutex. withLock {
180- // TODO: this is not yet specified, but it seems like the obvious right thing and it unlocks some integration tests; add spec point once specified
181- mutableState. data. count
178+ return result
179+ }
182180 }
183181 }
184182
185- internal var entries : [ ( key: String , value: LiveMapValue ) ] {
186- notYetImplemented ( )
187- }
188-
189183 internal var keys : [ String ] {
190- notYetImplemented ( )
184+ get throws ( ARTErrorInfo) {
185+ // RTLM12b: Identical to LiveMap#entries, except that it returns only the keys from the internal data map
186+ try entries. map ( \. key)
187+ }
191188 }
192189
193190 internal var values : [ LiveMapValue ] {
194- notYetImplemented ( )
191+ get throws ( ARTErrorInfo) {
192+ // RTLM13b: Identical to LiveMap#entries, except that it returns only the values from the internal data map
193+ try entries. map ( \. value)
194+ }
195195 }
196196
197197 internal func set( key _: String , value _: LiveMapValue ) async throws ( ARTErrorInfo) {
@@ -441,4 +441,63 @@ internal final class DefaultLiveMap: LiveMap {
441441 }
442442 }
443443 }
444+
445+ // MARK: - Helper Methods
446+
447+ /// Converts an ObjectsMapEntry to LiveMapValue using the same logic as get(key:)
448+ /// This is used by entries to ensure consistent value conversion
449+ private func convertEntryToLiveMapValue( _ entry: ObjectsMapEntry ) -> LiveMapValue ? {
450+ // RTLM5d2a: If ObjectsMapEntry.tombstone is true, return undefined/null
451+ // This is also equivalent to the RTLM14 check
452+ if entry. tombstone == true {
453+ return nil
454+ }
455+
456+ // Handle primitive values in the order specified by RTLM5d2b through RTLM5d2e
457+
458+ // RTLM5d2b: If ObjectsMapEntry.data.boolean exists, return it
459+ if let boolean = entry. data. boolean {
460+ return . primitive( . bool( boolean) )
461+ }
462+
463+ // RTLM5d2c: If ObjectsMapEntry.data.bytes exists, return it
464+ if let bytes = entry. data. bytes {
465+ return . primitive( . data( bytes) )
466+ }
467+
468+ // RTLM5d2d: If ObjectsMapEntry.data.number exists, return it
469+ if let number = entry. data. number {
470+ return . primitive( . number( number. doubleValue) )
471+ }
472+
473+ // RTLM5d2e: If ObjectsMapEntry.data.string exists, return it
474+ if let string = entry. data. string {
475+ switch string {
476+ case let . string( string) :
477+ return . primitive( . string( string) )
478+ case . json:
479+ // TODO: Understand how to handle JSON values (https://github.com/ably/specification/pull/333/files#r2164561055)
480+ notYetImplemented ( )
481+ }
482+ }
483+
484+ // RTLM5d2f: If ObjectsMapEntry.data.objectId exists, get the object stored at that objectId from the internal ObjectsPool
485+ if let objectId = entry. data. objectId {
486+ // RTLM5d2f1: If an object with id objectId does not exist, return undefined/null
487+ guard let poolEntry = delegate. referenced? . getObjectFromPool ( id: objectId) else {
488+ return nil
489+ }
490+
491+ // RTLM5d2f2: If an object with id objectId exists, return it
492+ switch poolEntry {
493+ case let . map( map) :
494+ return . liveMap( map)
495+ case let . counter( counter) :
496+ return . liveCounter( counter)
497+ }
498+ }
499+
500+ // RTLM5d2g: Otherwise, return undefined/null
501+ return nil
502+ }
444503}
0 commit comments