Skip to content

Commit

Permalink
remove compile error + make better for rust-analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Oct 4, 2024
1 parent 6e50923 commit 765f6a4
Show file tree
Hide file tree
Showing 13 changed files with 262 additions and 258 deletions.
124 changes: 75 additions & 49 deletions crates/objc2/src/__macro_helpers/os_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ const fn parse_usize(mut bytes: &[u8]) -> (usize, &[u8]) {
}

impl OSVersion {
const MIN: Self = Self {
major: 0,
minor: 0,
patch: 0,
};

const MAX: Self = Self {
major: u16::MAX,
minor: u8::MAX,
patch: u8::MAX,
};

/// Parse the version from a string at `const` time.
#[track_caller]
pub const fn from_str(version: &str) -> Self {
Expand Down Expand Up @@ -175,72 +187,75 @@ impl fmt::Debug for OSVersion {
/// with a compile error if the code ends up being compiled for that platform.
#[derive(Clone, Copy, Debug)]
pub struct AvailableVersion {
#[cfg(target_os = "macos")]
pub macos: OSVersion,
#[cfg(target_os = "ios")]
pub ios: OSVersion,
#[cfg(target_os = "tvos")]
pub tvos: OSVersion,
#[cfg(target_os = "watchos")]
pub watchos: OSVersion,
#[cfg(target_os = "visionos")]
pub visionos: OSVersion,
#[doc(hidden)]
pub __others: OSVersion,
}

impl AvailableVersion {
pub const DOTDOT_FALLBACK: Self = {
// A version of 0.0.0 will always be lower than the deployment target,
// and hence will always return `true` from `is_available`.
let _version = OSVersion {
major: 0,
minor: 0,
patch: 0,
};
Self {
#[cfg(target_os = "macos")]
macos: _version,
#[cfg(target_os = "ios")]
ios: _version,
#[cfg(target_os = "tvos")]
tvos: _version,
#[cfg(target_os = "watchos")]
watchos: _version,
#[cfg(target_os = "visionos")]
visionos: _version,
}
pub const MIN: Self = Self {
macos: OSVersion::MIN,
ios: OSVersion::MIN,
tvos: OSVersion::MIN,
watchos: OSVersion::MIN,
visionos: OSVersion::MIN,
__others: OSVersion::MIN,
};
}

#[inline]
#[cfg(not(target_vendor = "apple"))]
pub fn is_available(_version: AvailableVersion) -> bool {
// Assume that things are always available on GNUStep etc.
true
pub const MAX: Self = Self {
macos: OSVersion::MAX,
ios: OSVersion::MAX,
tvos: OSVersion::MAX,
watchos: OSVersion::MAX,
visionos: OSVersion::MAX,
__others: OSVersion::MAX,
};
}

#[inline]
#[cfg(target_vendor = "apple")]
pub fn is_available(version: AvailableVersion) -> bool {
#[cfg(target_os = "macos")]
let version = version.macos;
#[cfg(target_os = "ios")]
let version = version.ios;
#[cfg(target_os = "tvos")]
let version = version.tvos;
#[cfg(target_os = "watchos")]
let version = version.watchos;
#[cfg(target_os = "visionos")]
let version = version.visionos;

// If the deployment target is high enough, the API is always available.
let version = if cfg!(target_os = "macos") {
version.macos
} else if cfg!(target_os = "ios") {
version.ios
} else if cfg!(target_os = "tvos") {
version.tvos
} else if cfg!(target_os = "watchos") {
version.watchos
} else if cfg!(target_os = "visionos") {
version.visionos
} else {
version.__others
};

// In the special case that `version` was set to `OSVersion::MAX`, we
// assume that there can never be an OS version that large, and hence we
// want to avoid checking at all.
//
// This check should be optimized away at compile time.
if version <= apple::DEPLOYMENT_TARGET {
return true;
// This is useful for platforms where the version hasn't been specified.
if version == OSVersion::MAX {
return false;
}

// Otherwise, compare against the version at runtime.
version <= apple::current_version()
#[cfg(target_vendor = "apple")]
{
// If the deployment target is high enough, the API is always available.
//
// This check should be optimized away at compile time.
if version <= apple::DEPLOYMENT_TARGET {
return true;
}

// Otherwise, compare against the version at runtime.
version <= apple::current_version()
}

#[cfg(not(target_vendor = "apple"))]
return true;
}

#[cfg(test)]
Expand Down Expand Up @@ -361,6 +376,9 @@ mod tests {
// Always available
assert!(available!(..));

// Never available
assert!(!available!());

// Low versions, always available
assert!(available!(
macos = 10.0,
Expand All @@ -379,5 +397,13 @@ mod tests {
watchos = 99,
visionos = 99
));

if !cfg!(target_os = "tvos") {
// Available nowhere except tvOS
assert!(!available!(tvos = 1.2));

// Available everywhere, except low tvOS versions
assert!(available!(tvos = 1.2, ..));
}
}
}
11 changes: 2 additions & 9 deletions crates/objc2/src/__macro_helpers/os_version/apple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,15 +262,8 @@ mod tests {

#[test]
fn read_version() {
assert!(
current_version()
> OSVersion {
major: 0,
minor: 0,
patch: 0,
},
"version cannot be 0.0.0"
);
assert!(OSVersion::MIN < current_version(), "version cannot be min");
assert!(current_version() < OSVersion::MAX, "version cannot be max");
}

#[test]
Expand Down
167 changes: 30 additions & 137 deletions crates/objc2/src/macros/available.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,159 +10,52 @@
#[doc(alias = "#available")] // Swift
#[macro_export]
macro_rules! available {
(
$(
$os:ident = $major:literal $(. $minor:literal $(. $patch:literal)?)?
),+
$(,)?
) => {
$crate::__available_fields!(
()
$(
($os, $os, $crate::__available_version!($major $(. $minor $(. $patch)?)?))
)*
)
};
(
// Doesn't actually parse versions this way (see __available_version),
// but is helpful to write it like this for documentation.
$(
$os:ident = $major:literal $(. $minor:literal $(. $patch:literal)?)?,
)*
// Allow missing versions when `..` is specified, by spreading
// `AvailableVersion::DOTDOT_FALLBACK` in that case.
..
) => {
$crate::__available_fields!(
()
$(
($os, $os, $crate::__available_version!($major $(. $minor $(. $patch)?)?))
)*
..
)
};
}

/// tt-muncher, we need to handle each OS name manually, since `target_os`
/// only supports fixed literals (we can't do `target_os = stringify!($os)`).
#[doc(hidden)]
#[macro_export]
macro_rules! __available_fields {
// Base case
(
($($output:tt)*)
// Use optionality for the version here, to allow rust-analyzer to
// work with partially filled macros.
$os:ident $(= $major:literal $(. $minor:literal $(. $patch:literal)?)?)?
),*
$(,)?
) => {
$crate::__macro_helpers::is_available({
// TODO: Use inline const once in MSRV
#[allow(clippy::needless_update)]
const VERSION: $crate::__macro_helpers::AvailableVersion = $crate::__macro_helpers::AvailableVersion {
$($output)*
$(
$os: $($crate::__available_version!($major $(. $minor $(. $patch)?)?))?,
)*
// A version this high will never be lower than the deployment
// target, and hence will always return `false` from
// `is_available`.
.. $crate::__macro_helpers::AvailableVersion::MAX
};
VERSION
})
};
// macOS
(
($($output:tt)*)
(macos, $os:ident, $version:expr)
$($rest:tt)*
) => {
$crate::__available_fields!(
(
$($output)*
#[cfg(target_os = "macos")]
$os: $version,
)
$($rest)*
)
};
// iOS
(
($($output:tt)*)
(ios, $os:ident, $version:expr)
$($rest:tt)*
) => {
$crate::__available_fields!(
(
$($output)*
#[cfg(target_os = "ios")]
$os: $version,
)
$($rest)*
)
};
// tvOS
(
($($output:tt)*)
(tvos, $os:ident, $version:expr)
$($rest:tt)*
) => {
$crate::__available_fields!(
(
$($output)*
#[cfg(target_os = "tvos")]
$os: $version,
)
$($rest)*
)
};
// watchOS
(
($($output:tt)*)
(watchos, $os:ident, $version:expr)
$($rest:tt)*
) => {
$crate::__available_fields!(
(
$($output)*
#[cfg(target_os = "watchos")]
$os: $version,
)
$($rest)*
)
};
// visionOS
(
($($output:tt)*)
(visionos, $os:ident, $version:expr)
$($rest:tt)*
) => {
$crate::__available_fields!(
(
$($output)*
#[cfg(target_os = "visionos")]
$os: $version,
)
$($rest)*
)
};
// Unknown OS
(
($($output:tt)*)
($unknown_os:ident, $os:ident, $version:expr)
$($rest:tt)*
) => {
$crate::__available_fields!(
(
$($output)*
// It's fine to output the version for unknown OS-es here,
// since `AvailableVersion` is going to catch the mistake.
$os: $version,
)
$($rest)*
)
};
// .. marker.
(
($($output:tt)*)
$(
$os:ident $(= $major:literal $(. $minor:literal $(. $patch:literal)?)?)?,
)*
..
) => {
$crate::__available_fields!(
(
$($output)*
.. $crate::__macro_helpers::AvailableVersion::DOTDOT_FALLBACK
)
)
$crate::__macro_helpers::is_available({
#[allow(clippy::needless_update)]
const VERSION: $crate::__macro_helpers::AvailableVersion = $crate::__macro_helpers::AvailableVersion {
$(
$os: $($crate::__available_version!($major $(. $minor $(. $patch)?)?))?,
)*
// A version of 0.0.0 will always be lower than the deployment
// target, and hence will always return `true` from
// `is_available`.
//
// We do this when `..` is specified.
.. $crate::__macro_helpers::AvailableVersion::MIN
};
VERSION
})
};
}

Expand Down
Loading

0 comments on commit 765f6a4

Please sign in to comment.