@@ -93,4 +93,150 @@ mod tests {
9393 let decoded = from_msgpack ( & bytes) ;
9494 assert_eq ! ( value, decoded) ;
9595 }
96+
97+ #[ test]
98+ fn roundtrip_osc_message_like ( ) {
99+ // Construct an IrValue that matches the adapter's OSC message representation
100+ let value = IrValue :: Map ( vec ! [
101+ ( "$type" . into( ) , IrValue :: from( "osc.message" ) ) ,
102+ ( "address" . into( ) , IrValue :: from( "/test" ) ) ,
103+ (
104+ "args" . into( ) ,
105+ IrValue :: Array ( vec![
106+ IrValue :: Integer ( 7 ) ,
107+ IrValue :: Float ( 1.5 ) ,
108+ IrValue :: from( "text" ) ,
109+ IrValue :: Binary ( vec![ 1_u8 , 2 , 3 ] ) ,
110+ ] ) ,
111+ ) ,
112+ ] ) ;
113+
114+ let bytes = to_msgpack ( & value) ;
115+ let decoded = from_msgpack ( & bytes) ;
116+
117+ // Structure-preserving roundtrip
118+ assert_eq ! ( decoded, value) ;
119+
120+ // Extract and validate fields similar to adapter::try_extract_message
121+ let map = decoded. as_map ( ) . expect ( "expected map" ) ;
122+ let address = map. iter ( ) . find ( |( k, _) | k == "address" ) . unwrap ( ) . 1 . as_str ( ) ;
123+ assert_eq ! ( address, Some ( "/test" ) ) ;
124+
125+ let args = map
126+ . iter ( )
127+ . find ( |( k, _) | k == "args" )
128+ . unwrap ( )
129+ . 1
130+ . as_array ( )
131+ . expect ( "expected args array" ) ;
132+
133+ assert_eq ! ( args. len( ) , 4 ) ;
134+ assert_eq ! ( args[ 0 ] . as_integer( ) , Some ( 7 ) ) ;
135+ assert ! ( ( args[ 1 ] . as_float( ) . unwrap( ) - 1.5 ) . abs( ) < f64 :: EPSILON ) ;
136+ assert_eq ! ( args[ 2 ] . as_str( ) , Some ( "text" ) ) ;
137+ assert_eq ! ( args[ 3 ] . as_binary( ) , Some ( & [ 1_u8 , 2 , 3 ] [ ..] ) ) ;
138+ }
139+
140+ #[ test]
141+ fn msgpack_bytes_are_valid_and_match_contents ( ) {
142+ use std:: io:: Cursor ;
143+ use rmpv:: { decode:: read_value, Value } ;
144+
145+ // Prepare an OSC-like message map as IrValue
146+ let value = IrValue :: Map ( vec ! [
147+ ( "$type" . into( ) , IrValue :: from( "osc.message" ) ) ,
148+ ( "address" . into( ) , IrValue :: from( "/validate" ) ) ,
149+ (
150+ "args" . into( ) ,
151+ IrValue :: Array ( vec![
152+ IrValue :: Integer ( 123 ) ,
153+ IrValue :: Float ( -2.5 ) ,
154+ IrValue :: from( "ok" ) ,
155+ IrValue :: Binary ( vec![ 0xAA , 0xBB ] ) ,
156+ ] ) ,
157+ ) ,
158+ ] ) ;
159+
160+ // Encode to MessagePack
161+ let bytes = to_msgpack ( & value) ;
162+
163+ // Ensure bytes are valid MessagePack by decoding with rmpv
164+ let mut cursor = Cursor :: new ( & bytes) ;
165+ let root = read_value ( & mut cursor) . expect ( "must decode as msgpack Value" ) ;
166+
167+ // Helper: unwrap serde's externally tagged enum representation
168+ fn unwrap_enum ( v : & Value ) -> ( & str , & Value ) {
169+ match v {
170+ // Map form: { "Variant": payload }
171+ Value :: Map ( kv) if kv. len ( ) == 1 => {
172+ let ( k, v) = & kv[ 0 ] ;
173+ let name = match k { Value :: String ( s) => s. as_str ( ) . expect ( "variant name" ) , _ => panic ! ( "invalid enum key" ) } ;
174+ ( name, v)
175+ }
176+ // Array form: ["Variant", payload]
177+ Value :: Array ( items) if items. len ( ) == 2 => {
178+ let name = match & items[ 0 ] { Value :: String ( s) => s. as_str ( ) . expect ( "variant name" ) , _ => panic ! ( "invalid enum tag array" ) } ;
179+ ( name, & items[ 1 ] )
180+ }
181+ other => panic ! ( "unexpected enum encoding: {:?}" , other) ,
182+ }
183+ }
184+
185+ // Root must be the IrValue::Map(enum) variant
186+ let ( root_variant, root_payload) = unwrap_enum ( & root) ;
187+ assert_eq ! ( root_variant, "Map" ) ;
188+
189+ // Payload is Vec<(String, IrValue)> serialized as array of 2-element arrays
190+ let entries = match root_payload { Value :: Array ( a) => a, other => panic ! ( "expected entries array, got {:?}" , other) } ;
191+
192+ // Collect into hashmap-like view: key -> encoded IrValue
193+ let get = |key : & str | -> & Value {
194+ entries
195+ . iter ( )
196+ . find_map ( |entry| match entry {
197+ Value :: Array ( items) if items. len ( ) == 2 => match ( & items[ 0 ] , & items[ 1 ] ) {
198+ ( Value :: String ( s) , v) if s. as_str ( ) == Some ( key) => Some ( v) ,
199+ _ => None ,
200+ } ,
201+ _ => None ,
202+ } )
203+ . expect ( "entry not found" )
204+ } ;
205+
206+ // $type: IrValue::String("osc.message") -> enum String with payload string
207+ let ( ty_variant, ty_payload) = unwrap_enum ( get ( "$type" ) ) ;
208+ assert_eq ! ( ty_variant, "String" ) ;
209+ assert ! ( matches!( ty_payload, Value :: String ( s) if s. as_str( ) == Some ( "osc.message" ) ) ) ;
210+
211+ // address: IrValue::String("/validate")
212+ let ( addr_variant, addr_payload) = unwrap_enum ( get ( "address" ) ) ;
213+ assert_eq ! ( addr_variant, "String" ) ;
214+ assert ! ( matches!( addr_payload, Value :: String ( s) if s. as_str( ) == Some ( "/validate" ) ) ) ;
215+
216+ // args: IrValue::Array([...]) -> enum Array with payload array of encoded IrValue
217+ let ( args_variant, args_payload) = unwrap_enum ( get ( "args" ) ) ;
218+ assert_eq ! ( args_variant, "Array" ) ;
219+ let args = match args_payload { Value :: Array ( a) => a, other => panic ! ( "expected args payload array, got {:?}" , other) } ;
220+ assert_eq ! ( args. len( ) , 4 ) ;
221+
222+ // 0: Integer(123)
223+ let ( v0_variant, v0_payload) = unwrap_enum ( & args[ 0 ] ) ;
224+ assert_eq ! ( v0_variant, "Integer" ) ;
225+ assert ! ( matches!( v0_payload, Value :: Integer ( i) if i. as_i64( ) == Some ( 123 ) ) ) ;
226+
227+ // 1: Float(-2.5)
228+ let ( v1_variant, v1_payload) = unwrap_enum ( & args[ 1 ] ) ;
229+ assert_eq ! ( v1_variant, "Float" ) ;
230+ assert ! ( matches!( v1_payload, Value :: F64 ( x) if ( * x + 2.5 ) . abs( ) < f64 :: EPSILON ) ) ;
231+
232+ // 2: String("ok")
233+ let ( v2_variant, v2_payload) = unwrap_enum ( & args[ 2 ] ) ;
234+ assert_eq ! ( v2_variant, "String" ) ;
235+ assert ! ( matches!( v2_payload, Value :: String ( s) if s. as_str( ) == Some ( "ok" ) ) ) ;
236+
237+ // 3: Binary([0xAA, 0xBB])
238+ let ( v3_variant, v3_payload) = unwrap_enum ( & args[ 3 ] ) ;
239+ assert_eq ! ( v3_variant, "Binary" ) ;
240+ assert ! ( matches!( v3_payload, Value :: Binary ( b) if b. as_slice( ) == [ 0xAA , 0xBB ] ) ) ;
241+ }
96242}
0 commit comments