Repository: serialport/serialport-rs (language: rust)

Issue:
Dependency on unmaintained crates
This is minor, but I did not see any existing issue for it.

This crate depends on 3 unmaintained crates:

CoreFoundation-sys: https://github.com/dcuddeback/core-foundation-sys/issues/11
IOKit-sys: https://github.com/dcuddeback/iokit-sys/issues/13
mach: https://github.com/fitzgen/mach/issues/63, [RUSTSEC-2020-0168](https://rustsec.org/advisories/RUSTSEC-2020-0168.html)

```
Crate:     mach
Version:   0.1.2
Warning:   unmaintained
Title:     mach is unmaintained
Date:      2020-07-14
ID:        RUSTSEC-2020-0168
URL:       https://rustsec.org/advisories/RUSTSEC-2020-0168
Dependency tree:
mach 0.1.2
├── IOKit-sys 0.1.5
│   └── serialport 4.2.2-alpha.0
└── CoreFoundation-sys 0.1.4
    ├── serialport 4.2.2-alpha.0
    └── IOKit-sys 0.1.5
```



Discussion / hints:
Thank you for reporting this issue @newAM! Do you know if there are (de-facto) successors to these crates? Especially to IOKit-sys?
For `mach` there is [`mach2`](https://github.com/JohnTitor/mach2/tree/main).
For `IOKit-sys` and `CoreFoundation-sys` the leading alternative is [`apple-sys`](https://crates.io/crates/apple-sys).
I've gone ahead with using `apple-sys` here
https://github.com/serialport/serialport-rs/pull/125

Another alternative
https://github.com/servo/core-foundation-rs
> [...] For `IOKit-sys` and `CoreFoundation-sys` the leading alternative is [`apple-sys`](https://crates.io/crates/apple-sys).

Thank you pointing out these alternatives @newAM! Having a first glance at `apple-sys`, makes it stick out from other sys crates I know by generating is code on the fly where I was expecting a crate just containing the actual bindings.

As far as I'm overseeing this for now, we could experience issues at build time from an incompatible change in the APIs exposed from Apple's SDK but no breaking API from `serialport-rs`. I'm considering the former as having no impact as the APIs in question should be stable. If things get removed from these APIs, then we would have to handle this anyway. On top, we are using IOKit and CoreFoundation only for enumerating ports which also limits potential impact.

As we are not exposing anything from IOKit and CoreFoundation we are safe with respect to semver compatibility when Apple extends APIs with newer Framework versions.

We would lose the ability to build `serialport-rs` on a macOS system which does not have XCode command line tools installed. But it looks to me that this is no practical use case.

Has anyone of you already used this crate in production code and some experience?


> I've gone ahead with using `apple-sys` here #125

Thank you @tommy-gilligan for this PR! There are some build issues as of now but nothing which looks like we couldn't iron out quickly.


> Another alternative https://github.com/servo/core-foundation-rs

I've seen this one as well but it seems to just provide bindings for Core Foundation and not for IOKit. I found no alternative for the latter at a first glance and https://github.com/dcuddeback/iokit-sys/issues/13 has gone unanswered for about half a year. Does anyone of you know of one?
> We would lose the ability to build `serialport-rs` on a macOS system which does not have XCode command line tools installed. But it looks to me that this is no practical use case.

Adding a build-time dependency is a minor annoyance for package maintainers (e.g. nixpkgs for MacOS), but this is a pretty common requirement for building MacOS packages.  I doubt it would cause anyone problems.
> Thank you pointing out these alternatives @newAM! Having a first glance at apple-sys, makes it stick out from other sys crates I know by generating is code on the fly where I was expecting a crate just containing the actual bindings.

>We would lose the ability to build serialport-rs on a macOS system which does not have XCode command line tools installed. But it looks to me that this is no practical use case.

> Adding a build-time dependency is a minor annoyance for package maintainers (e.g. nixpkgs for MacOS), but this is a pretty common requirement for building MacOS packages. I doubt it would cause anyone problems.

This might be obvious but is there a reason why `apple-sys` doesn't release versioned crates of generated code?  Is this something it could do?
- I've taken a stab at just avoiding the Apple API on macOS.  Unfortunately, this option is not available for iOS. #126 
- I've also tried bringing IOKit to iOS in `apple-sys` but having trouble with that https://github.com/youknowone/apple-sys/pull/16 https://github.com/serialport/serialport-rs/pull/125
- Another alternative that I haven't tried yet: https://github.com/jtakakura/io-kit-rs
Looks like there is a crate downstream of `apple-sys` that needs updating for latest version of SDK https://github.com/indygreg/apple-platform-rs/blob/main/apple-sdk/
Thank you for all the research @tommy-gilligan!

> This might be obvious but is there a reason why `apple-sys` doesn't release versioned crates of generated code? Is this something it could do?

It looks to me that not including the binding code is a proclaimed goal of `apple-sys`: just maintaining the binding generator tools and not the actual generated code with will change over time.


>     * Another alternative that I haven't tried yet: https://github.com/jtakakura/io-kit-rs

This crate looks like the replacement for `IOKit-sys` I was looking for. I bet I missed it when looking at crates.io due to the dash in its name.

I would prefer using the curated `core-foundation-sys` in combination with `io-kit-sys` instead of `apple-sys` and will have a look a them the next days. I will also look into the other points and come back later.
Thank you @sirhcel !

> This might be obvious but is there a reason why apple-sys doesn't release versioned crates of generated code? Is this something it could do?

Sorry, reading this back, I feel like I completely butchered what I was trying to ask.

I was interested in: is it *technically* possible for somebody to use `apple-sys` as part of a pipeline to generate published crates eg. `apple-sys-core-foundation` maybe using IOS SDK version as crate version (for a more stable dependency)?  Maybe not a great question on reflection.  Basically anything is possible.

I maybe went a bit extra earlier and will calm my farm now to limit thread noise.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5cb1a0a6..70abc998 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,9 @@ project adheres to [Semantic Versioning](https://semver.org/).
   [#122](https://github.com/serialport/serialport-rs/pull/122)
 * Fixes a bug on Windows where some USB device serial numbers were truncated.
   [#131](https://github.com/serialport/serialport-rs/pull/131)
+* Switches to maintained sys crates for CoreFoundation and IOKit on macOS.
+  [#112](https://github.com/serialport/serialport-rs/issues/112),
+  [#136](https://github.com/serialport/serialport-rs/pull/136)
 
 ### Removed
 
diff --git a/Cargo.toml b/Cargo.toml
index 07f05306..7dff8eba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,8 +23,8 @@ nix = { version = "0.26", default-features = false, features = ["fs", "ioctl", "
 libudev = { version = "0.3.0", optional = true }
 
 [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
-CoreFoundation-sys = "0.1.4"
-IOKit-sys = "0.1.5"
+core-foundation-sys = "0.8.4"
+io-kit-sys = "0.4.0"
 mach2 = "0.4.1"
 
 [target."cfg(windows)".dependencies]
diff --git a/src/posix/enumerate.rs b/src/posix/enumerate.rs
index 53e4daf7..1c87ad3c 100644
--- a/src/posix/enumerate.rs
+++ b/src/posix/enumerate.rs
@@ -1,17 +1,25 @@
-#[cfg(any(target_os = "ios", target_os = "macos"))]
-use nix::libc::{c_char, c_void};
+use cfg_if::cfg_if;
+
 #[cfg(all(target_os = "linux", not(target_env = "musl"), feature = "libudev"))]
 use std::ffi::OsStr;
-#[cfg(any(target_os = "ios", target_os = "macos"))]
-use std::ffi::{CStr, CString};
-#[cfg(any(target_os = "ios", target_os = "macos"))]
-use std::mem::MaybeUninit;
 
-use cfg_if::cfg_if;
-#[cfg(any(target_os = "ios", target_os = "macos"))]
-use CoreFoundation_sys::*;
-#[cfg(any(target_os = "ios", target_os = "macos"))]
-use IOKit_sys::*;
+cfg_if! {
+    if #[cfg(any(target_os = "ios", target_os = "macos"))] {
+        use core_foundation_sys::base::*;
+        use core_foundation_sys::dictionary::*;
+        use core_foundation_sys::number::*;
+        use core_foundation_sys::string::*;
+        use io_kit_sys::*;
+        use io_kit_sys::keys::*;
+        use io_kit_sys::serial::keys::*;
+        use io_kit_sys::types::*;
+        use io_kit_sys::usb::lib::*;
+        use nix::libc::{c_char, c_void};
+        use std::convert::TryInto;
+        use std::ffi::{CStr, CString};
+        use std::mem::MaybeUninit;
+    }
+}
 
 #[cfg(any(
     target_os = "freebsd",
@@ -138,7 +146,7 @@ fn get_parent_device_by_type(
         }
         let mut parent = MaybeUninit::uninit();
         if unsafe {
-            IORegistryEntryGetParentEntry(device, kIOServiceClass(), parent.as_mut_ptr())
+            IORegistryEntryGetParentEntry(device, kIOServiceClass, parent.as_mut_ptr())
                 != KERN_SUCCESS
         } {
             return None;
@@ -224,7 +232,7 @@ fn get_string_property(device_type: io_registry_entry_t, property: &str) -> Opti
         let result = CFStringGetCString(
             container as CFStringRef,
             buf.as_mut_ptr(),
-            buf.capacity() as i64,
+            buf.capacity().try_into().unwrap(),
             kCFStringEncodingUTF8,
         );
         let opt_str = if result != 0 {
@@ -243,7 +251,7 @@ fn get_string_property(device_type: io_registry_entry_t, property: &str) -> Opti
 fn port_type(service: io_object_t) -> SerialPortType {
     let bluetooth_device_class_name = b"IOBluetoothSerialClient\0".as_ptr() as *const c_char;
     let usb_device_class_name = b"IOUSBHostDevice\0".as_ptr() as *const c_char;
-    let legacy_usb_device_class_name = kIOUSBDeviceClassName();
+    let legacy_usb_device_class_name = kIOUSBDeviceClassName;
 
     let maybe_usb_device = get_parent_device_by_type(service, usb_device_class_name)
         .or_else(|| get_parent_device_by_type(service, legacy_usb_device_class_name));
@@ -284,7 +292,7 @@ cfg_if! {
             let mut vec = Vec::new();
             unsafe {
                 // Create a dictionary for specifying the search terms against the IOService
-                let classes_to_match = IOServiceMatching(kIOSerialBSDServiceValue());
+                let classes_to_match = IOServiceMatching(kIOSerialBSDServiceValue);
                 if classes_to_match.is_null() {
                     return Err(Error::new(
                         ErrorKind::Unknown,
@@ -299,7 +307,7 @@ cfg_if! {
                 // searching for serial devices matching the RS232 device type.
                 let key = CFStringCreateWithCString(
                     kCFAllocatorDefault,
-                    kIOSerialBSDTypeKey(),
+                    kIOSerialBSDTypeKey,
                     kCFStringEncodingUTF8,
                 );
                 if key.is_null() {
@@ -314,7 +322,7 @@ cfg_if! {
 
                 let value = CFStringCreateWithCString(
                     kCFAllocatorDefault,
-                    kIOSerialBSDAllTypes(),
+                    kIOSerialBSDAllTypes,
                     kCFStringEncodingUTF8,
                 );
                 if value.is_null() {
@@ -382,38 +390,39 @@ cfg_if! {
 
                     if result == KERN_SUCCESS {
                         for key in ["IOCalloutDevice", "IODialinDevice"].iter() {
-                            let key = CString::new(*key).unwrap();
+                            let key_cstring = CString::new(*key).unwrap();
                             let key_cfstring = CFStringCreateWithCString(
                                 kCFAllocatorDefault,
-                                key.as_ptr(),
+                                key_cstring.as_ptr(),
                                 kCFStringEncodingUTF8,
                             );
                             let _key_cfstring_guard = scopeguard::guard((), |_| {
                                 CFRelease(key_cfstring as *const c_void);
                             });
 
-                            let value = CFDictionaryGetValue(props.assume_init(), key_cfstring as *const c_void);
-
-                            let type_id = CFGetTypeID(value);
-                            if type_id == CFStringGetTypeID() {
-                                let mut buf = Vec::with_capacity(256);
-
-                                CFStringGetCString(
-                                    value as CFStringRef,
-                                    buf.as_mut_ptr(),
-                                    256,
-                                    kCFStringEncodingUTF8,
-                                );
-                                let path = CStr::from_ptr(buf.as_ptr()).to_string_lossy();
-                                vec.push(SerialPortInfo {
-                                    port_name: path.to_string(),
-                                    port_type: port_type(modem_service),
-                                });
+                            let mut value = std::ptr::null();
+                            let found = CFDictionaryGetValueIfPresent(props.assume_init(), key_cfstring as *const c_void, &mut value);
+                            if found == true as Boolean {
+                                let type_id = CFGetTypeID(value);
+                                if type_id == CFStringGetTypeID() {
+                                    let mut buf = Vec::with_capacity(256);
+
+                                    CFStringGetCString(
+                                        value as CFStringRef,
+                                        buf.as_mut_ptr(),
+                                        buf.capacity() as isize,
+                                        kCFStringEncodingUTF8,
+                                    );
+                                    let path = CStr::from_ptr(buf.as_ptr()).to_string_lossy();
+                                    vec.push(SerialPortInfo {
+                                        port_name: path.to_string(),
+                                        port_type: port_type(modem_service),
+                                    });
+                                } else {
+                                    return Err(Error::new(ErrorKind::Unknown, "Found invalid type for TypeID"));
+                                }
                             } else {
-                                return Err(Error::new(
-                                    ErrorKind::Unknown,
-                                    "Found invalid type for TypeID",
-                                ));
+                                return Err(Error::new(ErrorKind::Unknown, format!("Key {} missing in dict", key)));
                             }
                         }
                     } else {


Test patch: