This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/599-provide-and-use-c-service-in-rust
in repository https://gitbox.apache.org/repos/asf/celix.git

commit 5329420835d1dc31f5f2b48e5d16ec1a3f7099a4
Author: Pepijn Noltes <pepijnnol...@gmail.com>
AuthorDate: Mon Aug 28 23:16:48 2023 +0200

     #599: Add poc svc tracker to rust experimental
---
 misc/experimental/rust/CMakeLists.txt              |  15 +-
 misc/experimental/rust/celix/src/bundle_context.rs | 463 ++++++++++++++++-----
 misc/experimental/rust/celix/src/errno.rs          |  10 +
 misc/experimental/rust/celix/src/lib.rs            |   1 +
 misc/experimental/rust/celix/src/log_helper.rs     | 111 ++---
 .../rust/celix_bindings/src/celix_bindings.h       |   1 -
 .../rust/shell_command_bundle/src/lib.rs           |  47 +--
 7 files changed, 462 insertions(+), 186 deletions(-)

diff --git a/misc/experimental/rust/CMakeLists.txt 
b/misc/experimental/rust/CMakeLists.txt
index 6ab2287c..966f83c8 100644
--- a/misc/experimental/rust/CMakeLists.txt
+++ b/misc/experimental/rust/CMakeLists.txt
@@ -16,7 +16,7 @@
 # under the License.
 
 option(CELIX_RUST_EXPERIMENTAL "Enable experimental rust bundle" OFF)
-if (CELIX_RUST_EXPERIMENTAL)
+if (CELIX_RUST_EXPERIMENTAL AND TARGET Celix::shell_tui AND TARGET 
Celix::shell AND TARGET Celix::log_admin)
     include(FetchContent)
     FetchContent_Declare(
             Corrosion
@@ -29,7 +29,7 @@ if (CELIX_RUST_EXPERIMENTAL)
     #Note for now this includes framework, utils and shell_api maybe this 
should be separated in the future.
     file(GENERATE OUTPUT
         "${CMAKE_CURRENT_BINARY_DIR}/include_paths.txt" CONTENT
-        
"$<TARGET_PROPERTY:framework,INTERFACE_INCLUDE_DIRECTORIES>;$<TARGET_PROPERTY:utils,INTERFACE_INCLUDE_DIRECTORIES>;$<TARGET_PROPERTY:shell_api,INTERFACE_INCLUDE_DIRECTORIES>;$<TARGET_PROPERTY:Celix::log_service_api,INTERFACE_INCLUDE_DIRECTORIES>;$<TARGET_PROPERTY:Celix::log_helper,INTERFACE_INCLUDE_DIRECTORIES>")
+        
"$<TARGET_PROPERTY:framework,INTERFACE_INCLUDE_DIRECTORIES>;$<TARGET_PROPERTY:utils,INTERFACE_INCLUDE_DIRECTORIES>;$<TARGET_PROPERTY:shell_api,INTERFACE_INCLUDE_DIRECTORIES>;$<TARGET_PROPERTY:Celix::log_service_api,INTERFACE_INCLUDE_DIRECTORIES>")
 
     corrosion_import_crate(MANIFEST_PATH Cargo.toml)
     corrosion_add_target_local_rustflags(rust_bundle_activator 
"-Cprefer-dynamic")
@@ -40,11 +40,17 @@ if (CELIX_RUST_EXPERIMENTAL)
     get_target_property(ACTUAL_LIB_TARGET rust_bundle_activator 
INTERFACE_LINK_LIBRARIES)
     add_celix_bundle(rust_bundle ACTIVATOR ${ACTUAL_LIB_TARGET})
     add_dependencies(rust_bundle rust_bundle_activator)
-
     corrosion_add_target_local_rustflags(rust_shell_command_activator 
"-Cprefer-dynamic")
-    corrosion_link_libraries(rust_shell_command_activator Celix::framework)
+    corrosion_link_libraries(rust_shell_command_activator
+            Celix::framework
+    )
     get_target_property(ACTUAL_LIB_TARGET rust_shell_command_activator 
INTERFACE_LINK_LIBRARIES)
     add_celix_bundle(rust_shell_command ACTIVATOR ${ACTUAL_LIB_TARGET})
+    target_link_libraries(${ACTUAL_LIB_TARGET} INTERFACE
+            -Wl,--whole-archive
+            Celix::log_helper
+            -Wl,--no-whole-archive
+    )
     add_dependencies(rust_shell_command rust_shell_command_activator)
 
     add_celix_container(rust_container NO_COPY
@@ -56,6 +62,7 @@ if (CELIX_RUST_EXPERIMENTAL)
 
     add_celix_container(rust_shell_cnt NO_COPY
         BUNDLES
+            Celix::log_admin
             Celix::shell
             Celix::shell_tui
             rust_shell_command
diff --git a/misc/experimental/rust/celix/src/bundle_context.rs 
b/misc/experimental/rust/celix/src/bundle_context.rs
index 56ddc69a..e944dfa4 100644
--- a/misc/experimental/rust/celix/src/bundle_context.rs
+++ b/misc/experimental/rust/celix/src/bundle_context.rs
@@ -21,12 +21,17 @@ use std::any::type_name;
 use std::any::Any;
 use std::collections::HashMap;
 use std::ffi::c_void;
+use std::ops::Deref;
 use std::ptr::null_mut;
 use std::sync::Arc;
 use std::sync::Mutex;
 use std::sync::Weak;
 
-use celix_bindings::celix_bundleContext_log;
+use celix_bindings::{celix_bundleContext_log, celix_bundleContext_stopTracker, 
celix_bundleContext_stopTrackerAsync, 
celix_bundleContext_trackServicesWithOptions, 
celix_bundleContext_trackServicesWithOptionsAsync, celix_properties_t};
+use celix_bindings::celix_service_filter_options;
+use celix_bindings::celix_bundleContext_registerServiceWithOptionsAsync;
+use celix_bindings::celix_bundleContext_unregisterServiceAsync;
+use celix_bindings::celix_service_tracking_options_t;
 use celix_bindings::celix_service_use_options_t;
 use celix_bindings::celix_service_filter_options_t;
 use celix_bindings::celix_bundleContext_useServicesWithOptions;
@@ -43,33 +48,64 @@ use super::LogLevel;
 
 pub struct ServiceRegistration {
     service_id: i64,
+    service_name: String,
+    unregister_async: bool,
     weak_ctx: Weak<BundleContext>,
-    _boxed_svc: Option<Box<dyn Any>>,
+    svc: Option<Box<dyn Any>>,
+    unmanaged_svc: Option<*mut dyn Any>,
+}
+
+impl ServiceRegistration {
+    pub fn get_service_id(&self) -> i64 {
+        self.service_id
+    }
+
+    pub fn get_service_name(&self) -> &str {
+        self.service_name.as_str()
+    }
+
+    pub fn get_service(&self) -> Option<&dyn Any> {
+        if let Some(boxed_svc) = self.svc.as_ref() {
+            Some(boxed_svc.as_ref())
+        } else if let Some(unmanaged_svc) = self.unmanaged_svc {
+            unsafe {
+                Some(&*unmanaged_svc)
+            }
+        } else {
+            None
+        }
+    }
 }
 
 impl Drop for ServiceRegistration {
     fn drop(&mut self) {
         let ctx = self.weak_ctx.upgrade();
         match ctx {
-            Some(ctx) => ctx.unregister_service(self.service_id),
+            Some(ctx) => ctx.unregister_service(self.service_id, 
self.unregister_async),
             None => println!("Cannot unregister ServiceRegistration: 
BundleContext is gone"),
         }
     }
 }
 
-pub struct ServiceRegistrationBuilder<'a> {
+pub struct ServiceRegistrationBuilder<'a, T: ?Sized + 'static> {
     ctx: &'a BundleContext,
-    boxed_svc: Option<Box<dyn Any>>,
+    register_async: bool,
+    unregister_async: bool,
+    svc: Option<Box<T>>, //note box is needed for stable pointer value
+    unmanaged_svc: Option<*mut dyn Any>,
     service_name: String,
     service_version: String,
     service_properties: HashMap<String, String>,
 }
 
-impl ServiceRegistrationBuilder<'_> {
-    fn new(ctx: &BundleContext) -> ServiceRegistrationBuilder {
+impl<T> ServiceRegistrationBuilder<'_, T> {
+    fn new(ctx: &BundleContext) -> ServiceRegistrationBuilder<T> {
         ServiceRegistrationBuilder {
             ctx,
-            boxed_svc: None,
+            register_async: false,
+            unregister_async: false,
+            svc: None,
+            unmanaged_svc: None,
             service_name: "".to_string(),
             service_version: "".to_string(),
             service_properties: HashMap::new(),
@@ -81,21 +117,21 @@ impl ServiceRegistrationBuilder<'_> {
         self
     }
 
-    fn with_service_name_if_not_set(&mut self, name: &str) -> &mut Self {
+    fn with_service_name_if_not_set(&mut self) -> &mut Self {
         if self.service_name.is_empty() {
-            self.service_name = name.to_string();
+            self.service_name = type_name::<T>().to_string();
         }
         self
     }
 
-    pub fn with_service<I: 'static>(&mut self, svc: I) -> &mut Self {
-        self.boxed_svc = Some(Box::new(svc));
-        self.with_service_name_if_not_set(type_name::<I>())
+    pub fn with_service(&mut self, svc: T) -> &mut Self {
+        self.svc = Some(Box::new(svc));
+        self.with_service_name_if_not_set()
     }
 
-    pub fn with_boxed_service<T: ?Sized + 'static>(&mut self, svc: Box<T>) -> 
&mut Self {
-        self.boxed_svc = Some(Box::new(svc));
-        self.with_service_name_if_not_set(type_name::<T>())
+    pub fn with_unmanaged_service(&mut self, svc: *mut T) -> &mut Self {
+        self.unmanaged_svc = Some(svc);
+        self.with_service_name_if_not_set()
     }
 
     pub fn with_version(&mut self, version: &str) -> &mut Self {
@@ -114,6 +150,26 @@ impl ServiceRegistrationBuilder<'_> {
         self
     }
 
+    pub fn with_register_async(&mut self) -> &mut Self {
+        self.register_async = true;
+        self
+    }
+
+    pub fn with_register_sync(&mut self) -> &mut Self {
+        self.register_async = false;
+        self
+    }
+
+    pub fn with_unregister_async(&mut self) -> &mut Self {
+        self.unregister_async = true;
+        self
+    }
+
+    pub fn with_unregister_sync(&mut self) -> &mut Self {
+        self.unregister_async = false;
+        self
+    }
+
     fn validate(&self) -> Result<(), Error> {
         let mut valid = true;
         if self.service_name.is_empty() {
@@ -121,7 +177,7 @@ impl ServiceRegistrationBuilder<'_> {
                 .log_error("Cannot register service. Service name is empty");
             valid = false;
         }
-        if self.boxed_svc.is_none() /*&& self.arc_svc.is_none() */  {
+        if self.svc.is_none() && self.unmanaged_svc.is_none() {
             self.ctx
                 .log_error("Cannot register service. No instance provided");
             valid = false;
@@ -132,25 +188,41 @@ impl ServiceRegistrationBuilder<'_> {
         }
     }
 
-    fn get_c_svc(&mut self) -> *mut c_void {
-        if let Some(boxed_svc) = self.boxed_svc.as_mut() {
-            let any_svc: &mut dyn Any = boxed_svc.as_mut();
-            let boxed_svc_ptr = any_svc as *mut dyn Any; //note box still owns 
the instance
-            boxed_svc_ptr as *mut c_void
+    fn get_c_svc(svc_reg: &ServiceRegistration) -> *mut c_void {
+        if let Some(boxed_svc) = svc_reg.svc.as_ref() {
+            boxed_svc.deref() as *const dyn Any as *mut c_void
+        } else if let Some(unmanaged_svc) = svc_reg.unmanaged_svc {
+            unmanaged_svc as *mut c_void
         } else {
             null_mut()
         }
     }
-
-    unsafe fn build_unsafe(&mut self, svc_ptr: *mut c_void) -> 
Result<ServiceRegistration, Error> {
+    
+    pub fn build(&mut self) -> Result<ServiceRegistration, Error> {
+        self.validate()?;
+        
+        let mut svc_reg = ServiceRegistration {
+            service_id: -1,
+            service_name: self.service_name.clone(),
+            unregister_async: self.unregister_async,
+            weak_ctx: self.ctx.get_self().clone(),
+            svc: if self.svc.is_none() { None } else { 
Some(self.svc.take().unwrap()) },
+            unmanaged_svc: self.unmanaged_svc,
+        };
+        
+        let svc_ptr = Self::get_c_svc(&svc_reg);
         let c_service_name = 
std::ffi::CString::new(self.service_name.as_str()).unwrap();
         let c_service_version = 
std::ffi::CString::new(self.service_version.as_str()).unwrap();
-        let c_service_properties = celix_properties_create();
-        for (key, value) in self.service_properties.iter() {
-            let c_key = std::ffi::CString::new(key.as_str()).unwrap();
-            let c_value = std::ffi::CString::new(value.as_str()).unwrap();
-            celix_properties_set(c_service_properties, c_key.as_ptr(), 
c_value.as_ptr());
+        let c_service_properties: *mut celix_properties_t;
+        unsafe {
+            c_service_properties = celix_properties_create();
+            for (key, value) in self.service_properties.iter() {
+                let c_key = std::ffi::CString::new(key.as_str()).unwrap();
+                let c_value = std::ffi::CString::new(value.as_str()).unwrap();
+                celix_properties_set(c_service_properties, c_key.as_ptr(), 
c_value.as_ptr());
+            }
         }
+
         let opts = celix_service_registration_options_t {
             svc: svc_ptr,
             factory: null_mut(),
@@ -166,71 +238,44 @@ impl ServiceRegistrationBuilder<'_> {
             asyncCallback: None,
         };
 
-        let service_id: i64 =
-            
celix_bundleContext_registerServiceWithOptions(self.ctx.get_c_bundle_context(), 
&opts);
-        if service_id >= 0 {
-            Ok(ServiceRegistration {
-                service_id,
-                weak_ctx: self.ctx.get_self().clone(),
-                _boxed_svc: self.boxed_svc.take(),
-            })
+        if self.register_async {
+            unsafe {
+                svc_reg.service_id = 
celix_bundleContext_registerServiceWithOptions(self.ctx.get_c_bundle_context(), 
&opts);
+            }
         } else {
-            Err(Error::BundleException)
+            unsafe {
+                svc_reg.service_id = 
celix_bundleContext_registerServiceWithOptionsAsync(self.ctx.get_c_bundle_context(),
 &opts);
+            }
         }
-    }
-
-    pub fn build(&mut self) -> Result<ServiceRegistration, Error> {
-        self.validate()?;
-        let svc_ptr = self.get_c_svc();
-        unsafe {
-            self.build_unsafe(svc_ptr)
+        if svc_reg.service_id >= 0 {
+            Ok(svc_reg)
+        } else {
+            Err(Error::BundleException)
         }
     }
 }
 
-pub struct ServiceUseBuilder<'a> {
+pub struct ServiceUseBuilder<'a, T> {
     ctx: &'a BundleContext,
     many: bool,
     service_name: String,
     filter: String,
-    callback: Option<Box<Box<dyn Fn(&dyn Any)>>>
+    callback: Option<Box<dyn Fn(&T)>>, //note double boxed
 }
 
-impl ServiceUseBuilder<'_> {
-    fn new(ctx: &BundleContext, many: bool) -> ServiceUseBuilder {
+impl<T> ServiceUseBuilder<'_, T> {
+    fn new(ctx: &BundleContext, many: bool) -> ServiceUseBuilder<T> {
         ServiceUseBuilder {
             ctx,
             many,
-            service_name: "".to_string(),
+            service_name: type_name::<T>().to_string(),
             filter: "".to_string(),
             callback: None,
         }
     }
 
-    pub fn with_service<T: ?Sized + 'static>(&mut self) -> &mut Self {
-        self.service_name = type_name::<T>().to_string();
-        self
-    }
-
-    #[doc = " @brief Provide a callback which will be called when a service is 
available. T must be a Sized (non trait) type, due to the use of downcast_ref"]
-    pub fn with_callback<T: Sized + 'static>(&mut self, closure: Box<dyn 
Fn(&T)>)-> &mut Self {
-        if self.service_name.is_empty() {
-            self.with_service::<T>();
-        }
-
-        let any_closure = Box::new(move |any_svc: &dyn Any| {
-            if let Some(svc) = any_svc.downcast_ref::<T>() {
-                closure(svc);
-            }
-        });
-
-        self.callback = Some(Box::new(any_closure));
-        self
-    }
-
-    #[doc = " @brief Provide a callback which will be called when a service is 
available, with a dyn Any argument. Note that this is useful for trait 
objects."]
-    pub fn with_any_callback(&mut self, closure: Box<dyn Fn(&dyn Any)>)-> &mut 
Self {
-        self.callback = Some(Box::new(closure)); //note double boxed
+    pub fn with_callback(&mut self, closure: Box<dyn Fn(&T)>)-> &mut Self {
+        self.callback = Some(closure);
         self
     }
 
@@ -245,17 +290,20 @@ impl ServiceUseBuilder<'_> {
     }
 
     fn validate(&self) -> Result<(), Error> {
-        if self.callback.is_none() || self.service_name.is_empty() {
+        if self.service_name.is_empty() {
             return Err(Error::BundleException);
         }
         Ok(())
     }
 
     unsafe extern "C" fn use_service_c_callback(handle: *mut c_void, svc: *mut 
c_void) {
-        let boxed_fn = Box::from_raw(handle as *mut Box<dyn Fn(&dyn Any)>);
-        let any_svc_ptr = svc as *mut dyn Any;
-        let any_svc_ref = any_svc_ptr.as_ref().unwrap();
-        boxed_fn(any_svc_ref);
+        let closure = handle as *const Box<dyn Fn(&T)>;
+        let closure = closure.as_ref().unwrap();
+
+        let typed_svc = svc as *const T;
+        let typed_svc = typed_svc.as_ref().unwrap();
+
+        closure(typed_svc);
     }
 
     pub fn build(&mut self) ->  Result<isize, Error> {
@@ -264,13 +312,9 @@ impl ServiceUseBuilder<'_> {
         let c_service_name = 
std::ffi::CString::new(self.service_name.as_str()).unwrap();
         let c_filter = std::ffi::CString::new(self.filter.as_str()).unwrap();
         let c_service_name_ptr: *const i8 = c_service_name.as_ptr();
-
-        //Note filter is for now unused, introduce when updating to use of 
celix_bundleContext_useServiceWithOptions
         let c_filter_ptr: *const i8 = if self.filter.is_empty() { null_mut()} 
else {c_filter.as_ptr() };
 
-        let boxed_fn = self.callback.take().unwrap();
-        let fn_ptr = Box::into_raw(boxed_fn) as *mut c_void;
-
+        let c_closure_ptr = self.callback.as_ref().unwrap() as *const Box<dyn 
Fn(&T)> as *mut c_void;
 
         let opts = celix_service_use_options_t {
             filter: celix_service_filter_options_t {
@@ -281,18 +325,20 @@ impl ServiceUseBuilder<'_> {
                 ignoreServiceLanguage: false,
             },
             waitTimeoutInSeconds: 0.0,
-            callbackHandle: fn_ptr,
+            callbackHandle: c_closure_ptr,
             use_: Some(Self::use_service_c_callback),
             useWithProperties: None,
             useWithOwner: None,
             flags: 0,
         };
 
-        unsafe {
-            if self.many {
+        if self.many {
+            unsafe {
                 let count = 
celix_bundleContext_useServicesWithOptions(self.ctx.get_c_bundle_context(), 
&opts);
                 Ok(count as isize)
-            } else {
+            }
+        } else {
+            unsafe {
                 let called = 
celix_bundleContext_useServiceWithOptions(self.ctx.get_c_bundle_context(), 
&opts);
                 if called {
                     Ok(1)
@@ -304,6 +350,210 @@ impl ServiceUseBuilder<'_> {
     }
 }
 
+struct ServiceTrackerCallbacks<T> {
+    set_callback: Option<Box<dyn Fn(Option<&T>)>>,
+    add_callback: Option<Box<dyn Fn(&T)>>,
+    remove_callback: Option<Box<dyn Fn(&T)>>,
+}
+
+pub struct ServiceTracker<T> {
+    ctx: Arc<BundleContext>,
+    tracker_id: i64,
+    // shared_data: Mutex<SharedServiceTrackerData<T>>,
+    // data_condition: Condvar,
+    callbacks: Box<ServiceTrackerCallbacks<T>>, //Note in a box to ensure 
pointer value is stable after move
+    stop_async: bool,
+}
+
+impl<T> ServiceTracker<T> {
+    pub fn close(&mut self) {
+        self.ctx.stop_tracker(self.tracker_id, self.stop_async);
+        self.tracker_id = -1;
+    }
+}
+
+impl<T> Drop for ServiceTracker<T> {
+    fn drop(&mut self) {
+        self.close();
+    }
+}
+
+pub struct ServiceTrackerBuilder<'a, T> {
+    ctx: &'a BundleContext,
+    service_name: String,
+    filter: String,
+    track_async: bool,
+    stop_async: bool,
+    set_callback: Option<Box<dyn Fn(Option<&T>)>>,
+    add_callback: Option<Box<dyn Fn(&T)>>,
+    remove_callback: Option<Box<dyn Fn(&T)>>,
+}
+
+impl<T> ServiceTrackerBuilder<'_, T> {
+    fn new(ctx: &BundleContext) -> ServiceTrackerBuilder<T> {
+        ServiceTrackerBuilder {
+            ctx,
+            service_name: type_name::<T>().to_string(),
+            filter: "".to_string(),
+            track_async: false,
+            stop_async: false,
+            set_callback: None,
+            add_callback: None,
+            remove_callback: None,
+        }
+    }
+
+    pub fn with_service_name(&mut self, name: &str) -> &mut Self {
+        self.service_name = name.to_string();
+        self
+    }
+
+    pub fn with_filter(&mut self, filter: &str) -> &mut Self {
+        self.filter = filter.to_string();
+        self
+    }
+
+    pub fn with_set_callback(&mut self, closure: Box<dyn Fn(Option<&T>)>) -> 
&mut Self {
+        self.set_callback = Some(closure);
+        self
+    }
+
+    pub fn with_add_callback(&mut self, closure: Box<dyn Fn(&T)>) -> &mut Self 
{
+        self.add_callback = Some(closure);
+        self
+    }
+
+    pub fn with_remove_callback(&mut self, closure: Box<dyn Fn(&T)>) -> &mut 
Self {
+        self.remove_callback = Some(closure);
+        self
+    }
+
+    pub fn with_track_async(&mut self) -> &mut Self {
+        self.track_async = true;
+        self
+    }
+
+    pub fn with_track_sync(&mut self) -> &mut Self {
+        self.track_async = false;
+        self
+    }
+
+    pub fn with_stop_async(&mut self) -> &mut Self {
+        self.stop_async = true;
+        self
+    }
+
+    pub fn with_stop_sync(&mut self) -> &mut Self {
+        self.stop_async = false;
+        self
+    }
+
+    fn validate(&self) -> Result<(), Error> {
+        if self.service_name.is_empty() {
+            return Err(Error::BundleException);
+        }
+        Ok(())
+    }
+
+    unsafe extern "C" fn set_callback_for_c(handle: *mut 
::std::os::raw::c_void, svc: *mut ::std::os::raw::c_void) {
+        let callbacks = handle as *const ServiceTrackerCallbacks<T>;
+        let callbacks = callbacks.as_ref().unwrap();
+
+        if svc.is_null() {
+            if let Some(set_callback) = callbacks.set_callback.as_ref() {
+                set_callback(None);
+            }
+        } else {
+            let typed_svc = svc as *const T;
+            let typed_svc = typed_svc.as_ref().unwrap();
+            if let Some(set_callback) = callbacks.set_callback.as_ref() {
+                set_callback(Some(typed_svc));
+            }
+        }
+    }
+
+    unsafe extern "C" fn add_callback_for_c(handle: *mut 
::std::os::raw::c_void, svc: *mut ::std::os::raw::c_void) {
+        let callbacks = handle as *const ServiceTrackerCallbacks<T>;
+        let callbacks = callbacks.as_ref().unwrap();
+
+        let typed_svc = svc as *const T;
+        let typed_svc = typed_svc.as_ref().unwrap();
+
+        if let Some(add_callback) = callbacks.add_callback.as_ref() {
+            add_callback(typed_svc);
+        }
+    }
+
+    unsafe extern "C" fn remove_callback_for_c(handle: *mut 
::std::os::raw::c_void, svc: *mut ::std::os::raw::c_void) {
+        let callbacks = handle as *const ServiceTrackerCallbacks<T>;
+        let callbacks = callbacks.as_ref().unwrap();
+
+        let typed_svc = svc as *const T;
+        let typed_svc = typed_svc.as_ref().unwrap();
+
+        if let Some(remove_callback) = callbacks.remove_callback.as_ref() {
+            remove_callback(typed_svc);
+        }
+    }
+
+    pub fn build(&mut self) -> Result<ServiceTracker<T>, Error> {
+        self.validate()?;
+
+        let mut svc_tracker = ServiceTracker {
+            ctx: self.ctx.get_self().upgrade().unwrap(),
+            tracker_id: -1,
+            callbacks: Box::new(ServiceTrackerCallbacks {
+                set_callback: self.set_callback.take(),
+                add_callback: self.add_callback.take(),
+                remove_callback: self.remove_callback.take(),
+            }),
+            stop_async: self.stop_async,
+        };
+
+        let c_service_name = 
std::ffi::CString::new(self.service_name.as_str()).unwrap();
+        let c_filter = std::ffi::CString::new(self.filter.as_str()).unwrap();
+        let c_callback_handle = svc_tracker.callbacks.as_ref() as *const 
ServiceTrackerCallbacks<T> as *mut c_void;
+
+        let opts = celix_service_tracking_options_t{
+            filter: celix_service_filter_options {
+                serviceName: c_service_name.as_ptr(),
+                versionRange: null_mut(),
+                filter: if self.filter.is_empty() { null_mut() } else { 
c_filter.as_ptr() },
+                serviceLanguage: null_mut(),
+                ignoreServiceLanguage: false,
+            },
+            callbackHandle: c_callback_handle,
+            set: Some(Self::set_callback_for_c),
+            setWithProperties: None,
+            setWithOwner: None,
+            add: Some(Self::add_callback_for_c),
+            addWithProperties: None,
+            addWithOwner: None,
+            remove: Some(Self::remove_callback_for_c),
+            removeWithProperties: None,
+            removeWithOwner: None,
+            trackerCreatedCallbackData: null_mut(),
+            trackerCreatedCallback: None,
+        };
+
+        let svc_tracker_id: i64;
+        unsafe {
+            if self.track_async {
+                svc_tracker_id = 
celix_bundleContext_trackServicesWithOptionsAsync(self.ctx.get_c_bundle_context(),
 &opts);
+            } else {
+                svc_tracker_id = 
celix_bundleContext_trackServicesWithOptions(self.ctx.get_c_bundle_context(), 
&opts);
+            }
+        }
+
+        if svc_tracker_id >= 0 {
+            svc_tracker.tracker_id = svc_tracker_id;
+            Ok(svc_tracker)
+        } else {
+            Err(Error::BundleException)
+        }
+    }
+}
+
 pub struct BundleContext {
     c_bundle_context: *mut celix_bundle_context_t,
     weak_self: Mutex<Option<Weak<BundleContext>>>,
@@ -329,12 +579,6 @@ impl BundleContext {
         self.weak_self.lock().unwrap().clone().unwrap()
     }
 
-    fn unregister_service(&self, service_id: i64) {
-        unsafe {
-            celix_bundleContext_unregisterService(self.c_bundle_context, 
service_id);
-        }
-    }
-
     fn log_to_c(&self, level: LogLevel, message: &str) {
         unsafe {
             let result = std::ffi::CString::new(message);
@@ -385,20 +629,39 @@ impl BundleContext {
         self.log(LogLevel::Fatal, message);
     }
 
-    //TODO make generic
-    pub fn register_service(&self) -> ServiceRegistrationBuilder {
+    pub fn register_service<T>(&self) -> ServiceRegistrationBuilder<T> {
         ServiceRegistrationBuilder::new(self)
     }
 
-    //TODO make generic
-    pub fn use_service(&self) -> ServiceUseBuilder {
+    fn unregister_service(&self, service_id: i64, unregister_async: bool) {
+        unsafe {
+            if unregister_async {
+                
celix_bundleContext_unregisterServiceAsync(self.c_bundle_context, service_id, 
null_mut(), None);
+            } else {
+                celix_bundleContext_unregisterService(self.c_bundle_context, 
service_id);
+            }
+        }
+    }
+
+    pub fn use_service<T>(&self) -> ServiceUseBuilder<T> {
         ServiceUseBuilder::new(self, false)
     }
 
-    //TODO make generic
-    pub fn use_services(&self) -> ServiceUseBuilder {
+    pub fn use_services<T>(&self) -> ServiceUseBuilder<T> {
         ServiceUseBuilder::new(self, true)
     }
+
+    pub fn track_services<T>(&self) -> ServiceTrackerBuilder<T> { 
ServiceTrackerBuilder::new(self) }
+
+    fn stop_tracker(&self, tracker_id: i64, stop_async: bool) {
+        unsafe {
+            if stop_async {
+                celix_bundleContext_stopTrackerAsync(self.c_bundle_context, 
tracker_id, null_mut(), None);
+            } else {
+                celix_bundleContext_stopTracker(self.c_bundle_context, 
tracker_id);
+            }
+        }
+    }
 }
 
 pub fn bundle_context_new(c_bundle_context: *mut celix_bundle_context_t) -> 
Arc<BundleContext> {
diff --git a/misc/experimental/rust/celix/src/errno.rs 
b/misc/experimental/rust/celix/src/errno.rs
index af23d2ca..4f9f85ee 100644
--- a/misc/experimental/rust/celix/src/errno.rs
+++ b/misc/experimental/rust/celix/src/errno.rs
@@ -17,6 +17,7 @@
  * under the License.
  */
 
+use std::fmt::{Debug, Formatter};
 use celix_bindings::celix_status_t;
 
 pub const CELIX_SUCCESS: celix_status_t = celix_bindings::CELIX_SUCCESS as 
celix_status_t;
@@ -47,3 +48,12 @@ impl Into<celix_status_t> for Error {
         }
     }
 }
+
+impl Debug for Error {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Error::BundleException => write!(f, "BundleException"),
+            Error::CelixStatusError(status) => write!(f, 
"CelixStatusError({})", status),
+        }
+    }
+}
diff --git a/misc/experimental/rust/celix/src/lib.rs 
b/misc/experimental/rust/celix/src/lib.rs
index b30f995e..390a6280 100644
--- a/misc/experimental/rust/celix/src/lib.rs
+++ b/misc/experimental/rust/celix/src/lib.rs
@@ -43,6 +43,7 @@ mod bundle_context;
 pub use self::bundle_context::bundle_context_new;
 pub use self::bundle_context::BundleContext;
 pub use self::bundle_context::ServiceRegistration;
+pub use self ::bundle_context::ServiceTracker;
 
 mod bundle_activator;
 // Re-export bundle activator types in the public API.
diff --git a/misc/experimental/rust/celix/src/log_helper.rs 
b/misc/experimental/rust/celix/src/log_helper.rs
index 64099ba5..72087eef 100644
--- a/misc/experimental/rust/celix/src/log_helper.rs
+++ b/misc/experimental/rust/celix/src/log_helper.rs
@@ -17,93 +17,98 @@
  * under the License.
  */
 
-use std::sync::Arc;
+use std::ops::Deref;
+use std::sync::{Arc, Mutex, RwLock};
 
 use super::BundleContext;
 use super::LogLevel;
 
-use celix_bindings::celix_logHelper_create;
-use celix_bindings::celix_logHelper_destroy;
-use celix_bindings::celix_logHelper_log;
-use celix_bindings::celix_log_helper_t;
+use celix_bindings::celix_log_service_t;
+use ServiceTracker;
+
 pub struct LogHelper {
-    celix_log_helper: *mut celix_log_helper_t,
+    name: String,
+    tracker: Mutex<Option<ServiceTracker<celix_log_service_t>>>,
+    log_svc: RwLock<Option<*const celix_log_service_t>>,
 }
 
 impl LogHelper {
-    pub fn new(ctx: Arc<BundleContext>, name: &str) -> Self {
-        unsafe {
-            let result = std::ffi::CString::new(name);
-            match result {
-                Ok(c_str) => LogHelper {
-                    celix_log_helper: celix_logHelper_create(
-                        ctx.get_c_bundle_context(),
-                        c_str.as_ptr() as *const i8,
-                    ),
-                },
-                Err(e) => {
-                    ctx.log_error(&format!(
-                        "Error creating CString: {}. Using \"error\" as log 
name",
-                        e
-                    ));
-                    let c_str = std::ffi::CString::new("error").unwrap();
-                    LogHelper {
-                        celix_log_helper: celix_logHelper_create(
-                            ctx.get_c_bundle_context(),
-                            c_str.as_ptr() as *const i8,
-                        ),
-                    }
+
+    pub fn new(ctx: Arc<BundleContext>, name: &str) -> Arc<Self> {
+        let helper = Arc::new(LogHelper{
+            name: name.to_string(),
+            tracker: Mutex::new(None),
+            log_svc: RwLock::new(None),
+        });
+        let filter = format!("(name={})", name);
+        let weak_helper = Arc::downgrade(&helper);
+        let tracker = ctx.track_services::<celix_log_service_t>()
+            .with_service_name("celix_log_service")
+            .with_filter(filter.as_str())
+            .with_set_callback(Box::new(move|optional_svc| {
+                if let Some(helper) = weak_helper.upgrade() {
+                    helper.set_celix_log_service(optional_svc);
                 }
+            }))
+            .build().unwrap();
+        helper.tracker.lock().unwrap().replace(tracker);
+        helper
+    }
+
+    pub fn get_name(&self) -> &str {
+        &self.name
+    }
+
+    pub fn set_celix_log_service(&self, optional_svc: 
Option<&celix_log_service_t>) {
+        match optional_svc {
+            Some(svc) => {
+                let svc_ptr: *const celix_log_service_t = svc as *const 
celix_log_service_t;
+                self.log_svc.write().unwrap().replace(svc_ptr);
+            }
+            None => {
+                self.log_svc.write().unwrap().take();
             }
         }
     }
 
     pub fn log(&self, level: LogLevel, message: &str) {
-        unsafe {
-            let result = std::ffi::CString::new(message);
-            match result {
-                Ok(c_str) => {
-                    celix_logHelper_log(
-                        self.celix_log_helper,
-                        level.into(),
-                        c_str.as_ptr() as *const i8,
-                    );
+        let str_result = std::ffi::CString::new(message).unwrap();
+        let guard = self.log_svc.read().unwrap();
+        if let Some(svc) = guard.as_ref() {
+            unsafe {
+                if svc.is_null() {
+                    return;
                 }
-                Err(e) => {
-                    println!("Error creating CString: {}", e);
+                let svc = &**svc;
+                if svc.log.is_none() {
+                    return;
                 }
+                let log_fn = svc.deref().log.as_ref().unwrap();
+                log_fn(svc.handle, level.into(), str_result.as_ptr());
             }
         }
     }
-    pub fn trace(&self, message: &str) {
+    pub fn log_trace(&self, message: &str) {
         self.log(LogLevel::Trace, message);
     }
 
-    pub fn debug(&self, message: &str) {
+    pub fn log_debug(&self, message: &str) {
         self.log(LogLevel::Debug, message);
     }
 
-    pub fn info(&self, message: &str) {
+    pub fn log_info(&self, message: &str) {
         self.log(LogLevel::Info, message);
     }
 
-    pub fn warning(&self, message: &str) {
+    pub fn log_warning(&self, message: &str) {
         self.log(LogLevel::Warning, message);
     }
 
-    pub fn error(&self, message: &str) {
+    pub fn log_error(&self, message: &str) {
         self.log(LogLevel::Error, message);
     }
 
-    pub fn fatal(&self, message: &str) {
+    pub fn log_fatal(&self, message: &str) {
         self.log(LogLevel::Fatal, message);
     }
 }
-
-impl Drop for LogHelper {
-    fn drop(&mut self) {
-        unsafe {
-            celix_logHelper_destroy(self.celix_log_helper);
-        }
-    }
-}
diff --git a/misc/experimental/rust/celix_bindings/src/celix_bindings.h 
b/misc/experimental/rust/celix_bindings/src/celix_bindings.h
index 5db2ff22..76c87b6a 100644
--- a/misc/experimental/rust/celix_bindings/src/celix_bindings.h
+++ b/misc/experimental/rust/celix_bindings/src/celix_bindings.h
@@ -33,4 +33,3 @@
 
 #include "celix_shell_command.h"
 #include "celix_log_service.h"
-#include "celix_log_helper.h"
\ No newline at end of file
diff --git a/misc/experimental/rust/shell_command_bundle/src/lib.rs 
b/misc/experimental/rust/shell_command_bundle/src/lib.rs
index 3856734e..bb097765 100644
--- a/misc/experimental/rust/shell_command_bundle/src/lib.rs
+++ b/misc/experimental/rust/shell_command_bundle/src/lib.rs
@@ -21,12 +21,11 @@ extern crate celix;
 extern crate celix_bindings;
 extern crate rust_shell_api;
 
-use std::any::Any;
 use std::ffi::c_char;
 use std::ffi::c_void;
 use std::sync::Arc;
 
-use celix::BundleActivator;
+use celix::{BundleActivator, LogHelper};
 use celix::BundleContext;
 use celix::Error;
 use rust_shell_api::{RustShellCommandTrait, RustShellCommandStruct};
@@ -92,7 +91,7 @@ impl RustShellCommandTrait for RustShellCommandImpl {
 
 struct ShellCommandActivator {
     ctx: Arc<BundleContext>,
-    //log_helper: Box<dyn LogHelper>,
+    log_helper: Arc<LogHelper>,
     shell_command_provider: CShellCommandImpl,
     registrations: Vec<celix::ServiceRegistration>,
 }
@@ -111,19 +110,17 @@ impl ShellCommandActivator {
             .with_property("command.description", "Simple command written in 
Rust")
             .build()?;
         self.registrations.push(registration);
-        self.ctx.log_info("C Shell Command registered");
+        self.log_helper.log_info("C Shell Command registered");
 
         //Register Rust trait service register using a Box
-        let rust_shell_command = 
Box::new(RustShellCommandImpl::new(self.ctx.clone()));
-        let rust_shell_command = Box::<dyn 
RustShellCommandTrait>::from(rust_shell_command);
+        let rust_shell_command: Arc<dyn RustShellCommandTrait> = 
Arc::new(RustShellCommandImpl::new(self.ctx.clone()));
         let registration = self.ctx.register_service()
-            //maybe make svc types more explicit, e.g.with type parameters
-            .with_boxed_service(rust_shell_command)
+            .with_service(rust_shell_command)
             .with_property(rust_shell_api::COMMAND_NAME, "exe_rust_command")
             .with_property(rust_shell_api::COMMAND_DESCRIPTION, "Simple 
command written in a Rust trait")
             .build()?;
         self.registrations.push(registration);
-        self.ctx.log_info("Rust trait Shell Command registered");
+        self.log_helper.log_info("Rust trait Shell Command registered");
 
 
         //Register Rust struct service (with closures) as value
@@ -140,53 +137,47 @@ impl ShellCommandActivator {
             .with_property("command.description", "Simple command written in a 
Rust struct using a Rust closure")
             .build()?;
         self.registrations.push(registration);
-        self.ctx.log_info("Rust struct Shell Command registered");
+        self.log_helper.log_info("Rust struct Shell Command registered");
         Ok(())
     }
 
     fn use_services(&mut self) -> Result<(), Error> {
         //test using C service
 
-        self.ctx.log_info("Use C service command service");
+        self.log_helper.log_info("Use C service command service");
         let count = self.ctx.use_services()
             .with_service_name("celix_shell_command")
             .with_filter("(command.name=exe_c_command_in_rust)")
             .with_callback(Box::new( |svc: &celix_shell_command_t| {
                 if let Some(exe_cmd) = svc.executeCommand {
-                    let c_str = std::ffi::CString::new("test").unwrap();
+                    let c_str = std::ffi::CString::new("test c 
service").unwrap();
                     unsafe {
                         exe_cmd(svc.handle, c_str.as_ptr() as *const c_char, 
std::ptr::null_mut(), std::ptr::null_mut());
                     }
                 }
             }))
             .build()?;
-        self.ctx.log_info(format!("Found {} celix_shell_command_t services", 
count).as_str());
+        self.log_helper.log_info(format!("Found {} celix_shell_command_t 
services", count).as_str());
 
         //test using Rust trait service
-        self.ctx.log_info("Use Rust trait service command service");
+        self.log_helper.log_info("Use Rust trait service command service");
         let count = self.ctx.use_services()
-            .with_service::<dyn RustShellCommandTrait>()
-            .with_any_callback(Box::new( |svc: &dyn Any| {
-                //Mote below downcast, this is a hack. Cannot downcast to 
trait.
-                //Fixme trait service users should not need impl type (impl 
details)
-                let typed_svc = svc.downcast_ref::<RustShellCommandImpl>();
-                if let Some(svc) = typed_svc {
-                    let _ = svc.execute_command("test");
-                }
+            .with_callback(Box::new( |svc: &Arc<dyn RustShellCommandTrait>| {
+                let _ = svc.execute_command("test rest trait");
             }))
             .build()?;
-        self.ctx.log_info(format!("Found {} RustShellCommandTrait services", 
count).as_str());
+        self.log_helper.log_info(format!("Found {} RustShellCommandTrait 
services", count).as_str());
 
-        self.ctx.log_info("Use Rust struct service command service");
+        self.log_helper.log_info("Use Rust struct service command service");
         let count = self.ctx.use_services()
             .with_callback(Box::new( |svc: &RustShellCommandStruct| {
                 let exe_cmd = svc.execute_command.as_ref();
-                let _ = exe_cmd("test");
+                let _ = exe_cmd("test rust struct");
             }))
             .build()?;
-        self.ctx.log_info(format!("Found {} RustShellCommandStruct services", 
count).as_str());
+        self.log_helper.log_info(format!("Found {} RustShellCommandStruct 
services", count).as_str());
 
-        self.ctx.log_info("Rust Shell Command started");
+        self.log_helper.log_info("Rust Shell Command started");
         Ok(())
     }
 }
@@ -196,7 +187,7 @@ impl BundleActivator for ShellCommandActivator {
         let result = ShellCommandActivator {
             ctx: ctx.clone(),
             shell_command_provider: CShellCommandImpl::new(ctx.clone()),
-            //log_helper: log_helper_new(&*ctx, "ShellCommandBundle"),
+            log_helper: LogHelper::new(ctx.clone(), "ShellCommandBundle"),
             registrations: Vec::new(),
         };
         result


Reply via email to