11use async_trait:: async_trait;
22use std:: sync:: Arc ;
3- use up_rust:: { UListener , UMessage , UStatus , UTransport , UUri } ;
3+ use up_rust:: { UCode , UListener , UMessage , UStatus , UTransport , UUri } ;
44
55/// This will be the main struct for our uProtocol transport.
66/// It will hold the state necessary to communicate with iceoryx2,
77/// such as the service connection and active listeners.
88pub struct Iceoryx2Transport { }
99
10+ enum MessageType {
11+ RpcRequest ,
12+ RpcResponseOrNotification ,
13+ Publish ,
14+ }
15+
1016// The #[async_trait] attribute enables async functions in our trait impl.
1117#[ async_trait]
1218impl UTransport for Iceoryx2Transport {
@@ -33,7 +39,181 @@ impl UTransport for Iceoryx2Transport {
3339 }
3440}
3541
42+ #[ allow( dead_code) ]
43+ impl Iceoryx2Transport {
44+ fn encode_uuri_segments ( uuri : & UUri ) -> Vec < String > {
45+ vec ! [
46+ uuri. authority_name. clone( ) ,
47+ Self :: encode_hex( uuri. uentity_type_id( ) as u32 ) ,
48+ Self :: encode_hex( uuri. uentity_instance_id( ) as u32 ) ,
49+ Self :: encode_hex( uuri. uentity_major_version( ) as u32 ) ,
50+ Self :: encode_hex( uuri. resource_id( ) as u32 ) ,
51+ ]
52+ }
53+
54+ fn encode_hex ( value : u32 ) -> String {
55+ format ! ( "{:X}" , value)
56+ }
57+
58+ /// Assumption: valid source and sink URIs provided:
59+ /// send() makes use of UAttributesValidator
60+ /// register_listener() and unregister_listener() use verify_filter_criteria()
61+ /// Criteria for identification of message types can be found here: https://github.com/eclipse-uprotocol/up-spec/blob/main/basics/uattributes.adoc
62+ fn determine_message_type ( source : & UUri , sink : Option < & UUri > ) -> Result < MessageType , UStatus > {
63+ let src_id = source. resource_id ;
64+ let sink_id = sink. map ( |s| s. resource_id ) ;
65+
66+ if src_id == 0 {
67+ if let Some ( id) = sink_id {
68+ if id >= 1 && id <= 0x7FFF {
69+ return Ok ( MessageType :: RpcRequest ) ;
70+ }
71+ }
72+ } else if sink_id == Some ( 0 ) && src_id >= 1 && src_id <= 0xFFFE {
73+ return Ok ( MessageType :: RpcResponseOrNotification ) ;
74+ } else if src_id >= 1 && src_id <= 0x7FFF {
75+ return Ok ( MessageType :: Publish ) ;
76+ }
77+
78+ Err ( UStatus :: fail_with_code (
79+ UCode :: INVALID_ARGUMENT ,
80+ "Unsupported UMessageType" ,
81+ ) )
82+ }
83+
84+ /// Called in send(), register_listener() and unregister_listener()
85+ fn compute_service_name ( source : & UUri , sink : Option < & UUri > ) -> Result < String , UStatus > {
86+ let join_segments = |segments : Vec < String > | segments. join ( "/" ) ;
87+
88+ match Self :: determine_message_type ( source, sink) ? {
89+ MessageType :: RpcRequest => {
90+ let Some ( sink_uri) = sink else {
91+ return Err ( UStatus :: fail_with_code (
92+ UCode :: INVALID_ARGUMENT ,
93+ "sink required for RpcRequest" ,
94+ ) ) ;
95+ } ;
96+ let segments = Self :: encode_uuri_segments ( sink_uri) ;
97+ Ok ( format ! ( "up/{}" , join_segments( segments) ) )
98+ }
99+ MessageType :: RpcResponseOrNotification => {
100+ let Some ( sink_uri) = sink else {
101+ return Err ( UStatus :: fail_with_code (
102+ UCode :: INVALID_ARGUMENT ,
103+ "sink required for ResponseOrNotification" ,
104+ ) ) ;
105+ } ;
106+ let source_segments = Self :: encode_uuri_segments ( source) ;
107+ let sink_segments = Self :: encode_uuri_segments ( sink_uri) ;
108+ Ok ( format ! (
109+ "up/{}/{}" ,
110+ join_segments( source_segments) ,
111+ join_segments( sink_segments)
112+ ) )
113+ }
114+ MessageType :: Publish => {
115+ let segments = Self :: encode_uuri_segments ( source) ;
116+ Ok ( format ! ( "up/{}" , join_segments( segments) ) )
117+ }
118+ }
119+ }
120+ }
121+
36122#[ cfg( test) ]
37123mod tests {
38124 use super :: * ;
125+
126+ fn test_uri ( authority : & str , instance : u16 , typ : u16 , version : u8 , resource : u16 ) -> UUri {
127+ let entity_id = ( ( instance as u32 ) << 16 ) | ( typ as u32 ) ;
128+ UUri :: try_from_parts ( authority, entity_id, version, resource) . unwrap ( )
129+ }
130+
131+ // performing successful tests for service name computation
132+
133+ #[ test]
134+ // [specitem,oft-sid="dsn~up-transport-iceoryx2-service-name~1",oft-needs="utest"]
135+ fn test_publish_service_name ( ) {
136+ let source = test_uri ( "device1" , 0x0000 , 0x10AB , 0x03 , 0x7FFF ) ;
137+
138+ let name = Iceoryx2Transport :: compute_service_name ( & source, None ) . unwrap ( ) ;
139+ assert_eq ! ( name, "up/device1/10AB/0/3/7FFF" ) ;
140+ }
141+
142+ #[ test]
143+ // [specitem,oft-sid="dsn~up-transport-iceoryx2-service-name~1",oft-needs="utest"]
144+ fn test_notification_service_name ( ) {
145+ let source = test_uri ( "device1" , 0x0000 , 0x10AB , 0x03 , 0x80CD ) ;
146+ let sink = test_uri ( "device1" , 0x0000 , 0x30EF , 0x04 , 0x0000 ) ;
147+ let name = Iceoryx2Transport :: compute_service_name ( & source, Some ( & sink) ) . unwrap ( ) ;
148+ assert_eq ! ( name, "up/device1/10AB/0/3/80CD/device1/30EF/0/4/0" ) ;
149+ }
150+
151+ #[ test]
152+ // [specitem,oft-sid="dsn~up-transport-iceoryx2-service-name~1",oft-needs="utest"]
153+ fn test_rpc_request_service_name ( ) {
154+ let sink = test_uri ( "device1" , 0x0004 , 0x03AB , 0x03 , 0x0000 ) ;
155+ let reply_to = test_uri ( "device1" , 0x0000 , 0x00CD , 0x04 , 0xB ) ;
156+
157+ let name = Iceoryx2Transport :: compute_service_name ( & sink, Some ( & reply_to) ) . unwrap ( ) ;
158+ assert_eq ! ( name, "up/device1/CD/0/4/B" ) ;
159+ }
160+
161+ #[ test]
162+ // [specitem,oft-sid="dsn~up-transport-iceoryx2-service-name~1",oft-needs="utest"]
163+ fn test_rpc_response_service_name ( ) {
164+ let source = test_uri ( "device1" , 0x0000 , 0x00CD , 0x04 , 0xB ) ;
165+ let sink = test_uri ( "device1" , 0x0004 , 0x3AB , 0x3 , 0x0000 ) ;
166+
167+ let name = Iceoryx2Transport :: compute_service_name ( & source, Some ( & sink) ) . unwrap ( ) ;
168+ assert_eq ! ( name, "up/device1/CD/0/4/B/device1/3AB/4/3/0" ) ;
169+ }
170+
171+ // performing failing tests for service name computation
172+
173+ #[ test]
174+ // .specitem[dsn~up-attributes-request-source~1]
175+ // .specitem[dsn~up-attributes-response-source~1]
176+ // .specitem[dsn~up-attributes-notification-source~1]
177+ fn test_missing_uri_error ( ) {
178+ let uuri = UUri :: new ( ) ;
179+ let result = Iceoryx2Transport :: compute_service_name ( & uuri, None ) ;
180+
181+ assert ! ( result. is_err( ) ) ;
182+ assert_eq ! ( result. unwrap_err( ) . get_code( ) , UCode :: INVALID_ARGUMENT ) ;
183+ }
184+
185+ #[ test]
186+ //both source and sink have resource ID equal to 0
187+ // .specitem[dsn~up-attributes-request-source~1]
188+ // .specitem[dsn~up-attributes-request-sink~1]
189+ // .specitem[dsn~up-attributes-response-source~1]
190+ // .specitem[dsn~up-attributes-response-sink~1]
191+ fn test_fail_resource_id_error ( ) {
192+ let source = test_uri ( "device1" , 0x0000 , 0x00CD , 0x04 , 0x000 ) ;
193+ let sink = test_uri ( "device1" , 0x0004 , 0x3AB , 0x3 , 0x0000 ) ;
194+ let result = Iceoryx2Transport :: compute_service_name ( & source, Some ( & sink) ) ;
195+ assert ! ( result. is_err_and( |err| err. get_code( ) == UCode :: INVALID_ARGUMENT ) ) ;
196+ }
197+
198+ #[ test]
199+ //source has resource id=0 but missing sink
200+ // .specitem[dsn~up-attributes-request-sink~1]
201+ // .specitem[dsn~up-attributes-request-source~1]
202+ fn test_fail_missing_sink_error ( ) {
203+ let source = test_uri ( "device1" , 0x0000 , 0x00CD , 0x04 , 0x000 ) ;
204+ let result = Iceoryx2Transport :: compute_service_name ( & source, None ) ;
205+ assert ! ( result. is_err_and( |err| err. get_code( ) == UCode :: INVALID_ARGUMENT ) ) ;
206+ }
207+
208+ #[ test]
209+ //missing source URI
210+ // .specitem[dsn~up-attributes-request-source~1]
211+ // .specitem[dsn~up-attributes-response-source~1]
212+ // .specitem[dsn~up-attributes-notification-source~1]
213+ fn test_fail_missing_source_error ( ) {
214+ let uuri = UUri :: new ( ) ;
215+ let sink = test_uri ( "device1" , 0x0004 , 0x3AB , 0x3 , 0x000 ) ;
216+ let result = Iceoryx2Transport :: compute_service_name ( & uuri, Some ( & sink) ) ;
217+ assert ! ( result. is_err_and( |err| err. get_code( ) == UCode :: INVALID_ARGUMENT ) ) ;
218+ }
39219}
0 commit comments