feat: add dependency

This commit is contained in:
2023-01-20 22:36:19 +08:00
parent 68e8d103b4
commit cf8e579f27
644 changed files with 150099 additions and 14 deletions

View File

@@ -0,0 +1,49 @@
//! Cross platform FFI helpers.
use std::ffi::CStr;
// The system property named 'persist.sys.timezone' contains the name of the
// current timezone.
//
// From https://android.googlesource.com/platform/bionic/+/gingerbread-release/libc/docs/OVERVIEW.TXT#79:
//
// > The name of the current timezone is taken from the TZ environment variable,
// > if defined. Otherwise, the system property named 'persist.sys.timezone' is
// > checked instead.
const ANDROID_TIMEZONE_PROPERTY_NAME: &[u8] = b"persist.sys.timezone\0";
/// Return a [`CStr`] to access the timezone from an Android system properties
/// environment.
pub(crate) fn android_timezone_property_name() -> &'static CStr {
// In tests or debug mode, opt into extra runtime checks.
if cfg!(any(test, debug_assertions)) {
return CStr::from_bytes_with_nul(ANDROID_TIMEZONE_PROPERTY_NAME).unwrap();
}
// SAFETY: the key is NUL-terminated and there are no other NULs, this
// invariant is checked in tests.
unsafe { CStr::from_bytes_with_nul_unchecked(ANDROID_TIMEZONE_PROPERTY_NAME) }
}
#[cfg(test)]
mod tests {
use std::ffi::CStr;
use super::{android_timezone_property_name, ANDROID_TIMEZONE_PROPERTY_NAME};
#[test]
fn test_android_timezone_property_name_is_valid_cstr() {
CStr::from_bytes_with_nul(ANDROID_TIMEZONE_PROPERTY_NAME).unwrap();
let mut invalid_property_name = ANDROID_TIMEZONE_PROPERTY_NAME.to_owned();
invalid_property_name.push(b'\0');
CStr::from_bytes_with_nul(&invalid_property_name).unwrap_err();
}
#[test]
fn test_android_timezone_property_name_getter() {
let key = android_timezone_property_name().to_bytes_with_nul();
assert_eq!(key, ANDROID_TIMEZONE_PROPERTY_NAME);
std::str::from_utf8(key).unwrap();
}
}

View File

@@ -0,0 +1,113 @@
#![warn(clippy::all)]
#![warn(clippy::cargo)]
#![warn(clippy::undocumented_unsafe_blocks)]
#![allow(unknown_lints)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused_qualifications)]
#![warn(variant_size_differences)]
//! get the IANA time zone for the current system
//!
//! This small utility crate provides the
//! [`get_timezone()`](fn.get_timezone.html) function.
//!
//! ```rust
//! // Get the current time zone as a string.
//! let tz_str = iana_time_zone::get_timezone()?;
//! println!("The current time zone is: {}", tz_str);
//! # Ok::<(), iana_time_zone::GetTimezoneError>(())
//! ```
//!
//! The resulting string can be parsed to a
//! [`chrono-tz::Tz`](https://docs.rs/chrono-tz/latest/chrono_tz/enum.Tz.html)
//! variant like this:
//! ```ignore
//! let tz_str = iana_time_zone::get_timezone()?;
//! let tz: chrono_tz::Tz = tz_str.parse()?;
//! ```
#[allow(dead_code)]
mod ffi_utils;
#[cfg_attr(target_os = "linux", path = "tz_linux.rs")]
#[cfg_attr(target_os = "windows", path = "tz_windows.rs")]
#[cfg_attr(any(target_os = "macos", target_os = "ios"), path = "tz_macos.rs")]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
path = "tz_wasm32.rs"
)]
#[cfg_attr(
any(target_os = "freebsd", target_os = "dragonfly"),
path = "tz_freebsd.rs"
)]
#[cfg_attr(
any(target_os = "netbsd", target_os = "openbsd"),
path = "tz_netbsd.rs"
)]
#[cfg_attr(
any(target_os = "illumos", target_os = "solaris"),
path = "tz_illumos.rs"
)]
#[cfg_attr(target_os = "android", path = "tz_android.rs")]
#[cfg_attr(target_os = "haiku", path = "tz_haiku.rs")]
mod platform;
/// Error types
#[derive(Debug)]
pub enum GetTimezoneError {
/// Failed to parse
FailedParsingString,
/// Wrapped IO error
IoError(std::io::Error),
/// Platform-specific error from the operating system
OsError,
}
impl std::error::Error for GetTimezoneError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
GetTimezoneError::FailedParsingString => None,
GetTimezoneError::IoError(err) => Some(err),
GetTimezoneError::OsError => None,
}
}
}
impl std::fmt::Display for GetTimezoneError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.write_str(match self {
GetTimezoneError::FailedParsingString => "GetTimezoneError::FailedParsingString",
GetTimezoneError::IoError(err) => return err.fmt(f),
GetTimezoneError::OsError => "OsError",
})
}
}
impl From<std::io::Error> for GetTimezoneError {
fn from(orig: std::io::Error) -> Self {
GetTimezoneError::IoError(orig)
}
}
/// Get the current IANA time zone as a string.
///
/// See the module-level documentatation for a usage example and more details
/// about this function.
#[inline]
pub fn get_timezone() -> Result<String, GetTimezoneError> {
platform::get_timezone_inner()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_current() {
println!("current: {}", get_timezone().unwrap());
}
}

View File

@@ -0,0 +1,9 @@
pub fn get_timezone_inner() -> std::result::Result<String, crate::GetTimezoneError> {
Err(crate::GetTimezoneError::OsError)
}
#[cfg(not(feature = "fallback"))]
compile_error!(
"iana-time-zone is currently implemented for Linux, Window, MacOS, FreeBSD, NetBSD, \
OpenBSD, Dragonfly, WebAssembly (browser), iOS, Illumos, Android, Solaris and Haiku.",
);

View File

@@ -0,0 +1,27 @@
use std::sync::Once;
use android_system_properties::AndroidSystemProperties;
use crate::ffi_utils::android_timezone_property_name;
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
let key = android_timezone_property_name();
get_properties()
.and_then(|properties| properties.get_from_cstr(key))
.ok_or(crate::GetTimezoneError::OsError)
}
fn get_properties() -> Option<&'static AndroidSystemProperties> {
static INITIALIZED: Once = Once::new();
static mut PROPERTIES: Option<AndroidSystemProperties> = None;
INITIALIZED.call_once(|| {
let properties = AndroidSystemProperties::new();
// SAFETY: `INITIALIZED` is synchronizing. The variable is only assigned to once.
unsafe { PROPERTIES = Some(properties) };
});
// SAFETY: `INITIALIZED` is synchronizing. The variable is only assigned to once.
unsafe { PROPERTIES.as_ref() }
}

View File

@@ -0,0 +1,7 @@
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
// see https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/19
let mut contents = std::fs::read_to_string("/var/db/zoneinfo")?;
// Trim to the correct length without allocating.
contents.truncate(contents.trim_end().len());
Ok(contents)
}

View File

@@ -0,0 +1,3 @@
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
iana_time_zone_haiku::get_timezone().ok_or(crate::GetTimezoneError::OsError)
}

View File

@@ -0,0 +1,22 @@
use std::fs::OpenOptions;
use std::io::{BufRead, BufReader};
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
// https://illumos.org/man/5/TIMEZONE
// https://docs.oracle.com/cd/E23824_01/html/821-1473/uc-timezone-4.html
let file = OpenOptions::new().read(true).open("/etc/default/init")?;
let mut reader = BufReader::with_capacity(1536, file);
let mut line = String::with_capacity(80);
loop {
line.clear();
let count = reader.read_line(&mut line)?;
if count == 0 {
return Err(crate::GetTimezoneError::FailedParsingString);
} else if line.starts_with("TZ=") {
line.truncate(line.trim_end().len());
line.replace_range(..3, "");
return Ok(line);
}
}
}

View File

@@ -0,0 +1,45 @@
use std::fs::{read_link, read_to_string};
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
etc_localtime().or_else(|_| etc_timezone())
}
fn etc_timezone() -> Result<String, crate::GetTimezoneError> {
// see https://stackoverflow.com/a/12523283
let mut contents = read_to_string("/etc/timezone")?;
// Trim to the correct length without allocating.
contents.truncate(contents.trim_end().len());
Ok(contents)
}
fn etc_localtime() -> Result<String, crate::GetTimezoneError> {
// Per <https://www.man7.org/linux/man-pages/man5/localtime.5.html>:
// “ The /etc/localtime file configures the system-wide timezone of the local system that is
// used by applications for presentation to the user. It should be an absolute or relative
// symbolic link pointing to /usr/share/zoneinfo/, followed by a timezone identifier such as
// "Europe/Berlin" or "Etc/UTC". The resulting link should lead to the corresponding binary
// tzfile(5) timezone data for the configured timezone. ”
// Systemd does not canonicalize the link, but only checks if it is prefixed by
// "/usr/share/zoneinfo/" or "../usr/share/zoneinfo/". So we do the same.
// <https://github.com/systemd/systemd/blob/9102c625a673a3246d7e73d8737f3494446bad4e/src/basic/time-util.c#L1493>
const PREFIXES: &[&str] = &[
"/usr/share/zoneinfo/", // absolute path
"../usr/share/zoneinfo/", // relative path
"/etc/zoneinfo/", // absolute path for NixOS
"../etc/zoneinfo/", // relative path for NixOS
];
let mut s = read_link("/etc/localtime")?
.into_os_string()
.into_string()
.map_err(|_| crate::GetTimezoneError::FailedParsingString)?;
for &prefix in PREFIXES {
if s.starts_with(prefix) {
// Trim to the correct length without allocating.
s.replace_range(..prefix.len(), "");
return Ok(s);
}
}
Err(crate::GetTimezoneError::FailedParsingString)
}

View File

@@ -0,0 +1,144 @@
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
get_timezone().ok_or(crate::GetTimezoneError::OsError)
}
#[inline]
fn get_timezone() -> Option<String> {
// The longest name in the IANA time zone database is 25 ASCII characters long.
const MAX_LEN: usize = 32;
let mut buf = [0; MAX_LEN];
// Get system time zone, and borrow its name.
let tz = system_time_zone::SystemTimeZone::new()?;
let name = tz.name()?;
// If the name is encoded in UTF-8, copy it directly.
let name = if let Some(name) = name.as_utf8() {
name
} else {
// Otherwise convert the name to UTF-8.
name.to_utf8(&mut buf)?
};
if name.is_empty() || name.len() >= MAX_LEN {
// The name should not be empty, or excessively long.
None
} else {
Some(name.to_owned())
}
}
mod system_time_zone {
//! create a safe wrapper around `CFTimeZoneRef`
use core_foundation_sys::base::{CFRelease, CFTypeRef};
use core_foundation_sys::timezone::{CFTimeZoneCopySystem, CFTimeZoneGetName, CFTimeZoneRef};
pub(crate) struct SystemTimeZone(CFTimeZoneRef);
impl Drop for SystemTimeZone {
fn drop(&mut self) {
// SAFETY: `SystemTimeZone` is only ever created with a valid `CFTimeZoneRef`.
unsafe { CFRelease(self.0 as CFTypeRef) };
}
}
impl SystemTimeZone {
pub(crate) fn new() -> Option<Self> {
// SAFETY: No invariants to uphold. We'll release the pointer when we don't need it anymore.
let v: CFTimeZoneRef = unsafe { CFTimeZoneCopySystem() };
if v.is_null() {
None
} else {
Some(SystemTimeZone(v))
}
}
/// Get the time zone name as a [super::string_ref::StringRef].
///
/// The lifetime of the `StringRef` is bound to our lifetime. Mutable
/// access is also prevented by taking a reference to `self`.
pub(crate) fn name(&self) -> Option<super::string_ref::StringRef<'_, Self>> {
// SAFETY: `SystemTimeZone` is only ever created with a valid `CFTimeZoneRef`.
let string = unsafe { CFTimeZoneGetName(self.0) };
if string.is_null() {
None
} else {
// SAFETY: here we ensure that `string` is a valid pointer.
Some(unsafe { super::string_ref::StringRef::new(string, self) })
}
}
}
}
mod string_ref {
//! create safe wrapper around `CFStringRef`
use std::convert::TryInto;
use core_foundation_sys::base::{Boolean, CFRange};
use core_foundation_sys::string::{
kCFStringEncodingUTF8, CFStringGetBytes, CFStringGetCStringPtr, CFStringGetLength,
CFStringRef,
};
pub(crate) struct StringRef<'a, T> {
string: CFStringRef,
// We exclude mutable access to the parent by taking a reference to the
// parent (rather than, for example, just using a marker to enforce the
// parent's lifetime).
_parent: &'a T,
}
impl<'a, T> StringRef<'a, T> {
// SAFETY: `StringRef` must be valid pointer
pub(crate) unsafe fn new(string: CFStringRef, _parent: &'a T) -> Self {
Self { string, _parent }
}
pub(crate) fn as_utf8(&self) -> Option<&'a str> {
// SAFETY: `StringRef` is only ever created with a valid `CFStringRef`.
let v = unsafe { CFStringGetCStringPtr(self.string, kCFStringEncodingUTF8) };
if !v.is_null() {
// SAFETY: `CFStringGetCStringPtr()` returns NUL-terminated strings.
let v = unsafe { std::ffi::CStr::from_ptr(v) };
if let Ok(v) = v.to_str() {
return Some(v);
}
}
None
}
pub(crate) fn to_utf8<'b>(&self, buf: &'b mut [u8]) -> Option<&'b str> {
// SAFETY: `StringRef` is only ever created with a valid `CFStringRef`.
let length = unsafe { CFStringGetLength(self.string) };
let mut buf_bytes = 0;
let range = CFRange {
location: 0,
length,
};
let converted_bytes = unsafe {
// SAFETY: `StringRef` is only ever created with a valid `CFStringRef`.
CFStringGetBytes(
self.string,
range,
kCFStringEncodingUTF8,
b'\0',
false as Boolean,
buf.as_mut_ptr(),
buf.len() as isize,
&mut buf_bytes,
)
};
if converted_bytes != length {
return None;
}
let len = buf_bytes.try_into().ok()?;
let s = buf.get(..len)?;
std::str::from_utf8(s).ok()
}
}
}

View File

@@ -0,0 +1,25 @@
use std::fs::read_link;
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
// see https://www.cyberciti.biz/faq/openbsd-time-zone-howto/
// This is a backport of the Linux implementation.
// NetBSDs is less than thorough how the softlink should be set up.
const PREFIXES: &[&str] = &[
"/usr/share/zoneinfo/", // absolute path
"../usr/share/zoneinfo/", // relative path
];
let mut s = read_link("/etc/localtime")?
.into_os_string()
.into_string()
.map_err(|_| crate::GetTimezoneError::FailedParsingString)?;
for &prefix in PREFIXES {
if s.starts_with(prefix) {
// Trim to the correct length without allocating.
s.replace_range(..prefix.len(), "");
return Ok(s);
}
}
Err(crate::GetTimezoneError::FailedParsingString)
}

View File

@@ -0,0 +1,21 @@
use js_sys::{Array, Intl, Object, Reflect};
use wasm_bindgen::JsValue;
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
let intl = Intl::DateTimeFormat::new(&Array::new(), &Object::new()).resolved_options();
Reflect::get(&intl, &JsValue::from_str("timeZone"))
.ok()
.and_then(|tz| tz.as_string())
.ok_or(crate::GetTimezoneError::OsError)
}
#[cfg(test)]
mod tests {
use wasm_bindgen_test::*;
#[wasm_bindgen_test]
fn pass() {
let tz = super::get_timezone_inner().unwrap();
console_log!("tz={:?}", tz);
}
}

View File

@@ -0,0 +1,312 @@
use std::mem::zeroed;
use windows_sys::core::{GUID, HRESULT};
use windows_sys::Win32::System::Com::CoIncrementMTAUsage;
use windows_sys::Win32::System::WinRT::HSTRING_HEADER;
use self::hstring::HString;
use self::instance::Instance;
use self::tz_on_calendar::TzOnCalendar;
macro_rules! wstring {
($($letters:tt)+) => {
[ $($letters as _,)+ ]
};
}
const WINDOWS_GLOBALIZATION_CALENDAR: &[u16] = &wstring!(
'W' 'i' 'n' 'd' 'o' 'w' 's' '.'
'G' 'l' 'o' 'b' 'a' 'l' 'i' 'z' 'a' 't' 'i' 'o' 'n' '.'
'C' 'a' 'l' 'e' 'n' 'd' 'a' 'r'
0
);
const TIMEZONE_ON_CALENDAR_GUID: GUID = GUID {
data1: 0xbb3c25e5,
data2: 0x46cf,
data3: 0x4317,
data4: [0xa3, 0xf5, 0x02, 0x62, 0x1a, 0xd5, 0x44, 0x78],
};
const CO_E_NOTINITIALIZED: HRESULT = -2147221008;
impl From<HRESULT> for crate::GetTimezoneError {
fn from(orig: HRESULT) -> Self {
std::io::Error::from_raw_os_error(orig).into()
}
}
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
// Get HSTRING for "Windows.Globalization.Calendar".
// SAFETY: An `HSTRING_HEADER` actually does not need initialization when used with
// `WindowsCreateStringReference()`, but zeroing it is as good a initial value as any.
let mut string_header: HSTRING_HEADER = unsafe { zeroed() };
let class_name = HString::create(WINDOWS_GLOBALIZATION_CALENDAR, &mut string_header)?;
// Create new "Windows.Globalization.Calendar" instance.
let calendar = Instance::activate(&class_name).or_else(|result| {
// Some other library could have called CoIncrementMTAUsage() or CoInitializeEx(), so we
// only call CoIncrementMTAUsage() if RoActivateInstance() tells us that multithreading
// was not initialized, yet.
// No need to check the error. The only conceivable error code this function returns is
// E_OUTOFMEMORY, and the program is about to get OOM killed anyway in this case.
// Windows-rs does not check the result, either.
if result != CO_E_NOTINITIALIZED {
return Err(result);
}
let mut cookie = 0;
// SAFETY: "Don't call CoIncrementMTAUsage during process shutdown or inside dllmain."
// Using the function is `fn main()` is totally fine. If you go really low level
// and implement an "fn wWinMain()" somehow, then all bets are off anyway.
let _ = unsafe { CoIncrementMTAUsage(&mut cookie) };
Instance::activate(&class_name)
})?;
// Query ITimeZoneOnCalendar of the calendar instance.
let tz = TzOnCalendar::query(&calendar)?;
// Get the name of the time zone.
let name = HString::from_tz_on_calendar(&tz)?;
// Convert to Rust String
Ok(name.to_string())
}
mod hstring {
use std::ptr::null_mut;
use windows_sys::core::{HRESULT, HSTRING};
use windows_sys::Win32::System::WinRT::WindowsDeleteString;
use windows_sys::Win32::System::WinRT::{
WindowsCreateStringReference, WindowsGetStringRawBuffer, HSTRING_HEADER,
};
use super::tz_on_calendar::TzOnCalendar;
pub struct HString<'a> {
string: HSTRING,
_header: Option<&'a mut HSTRING_HEADER>,
}
impl<'a> HString<'a> {
// `source` must be null-terminated. Windows tests if the the terminator is missing and
// returns an error if it is absent.
pub fn create(source: &'a [u16], header: &'a mut HSTRING_HEADER) -> Result<Self, HRESULT> {
let mut string = null_mut();
// SAFETY: `source` is a valid reference. If its contents are not a valid wide string,
// then the call will return an error code. We keep a reference to the `source`
// and `header`, so they stay valid until the `HSTRING` is released.
let result = unsafe {
WindowsCreateStringReference(
source.as_ptr(),
(source.len().saturating_sub(1)) as u32,
header,
&mut string,
)
};
if result < 0 || string.is_null() {
Err(result)
} else {
Ok(Self {
string,
_header: Some(header),
})
}
}
pub fn from_tz_on_calendar(instance: &'a TzOnCalendar) -> Result<Self, HRESULT> {
let mut string = null_mut();
// SAFETY: A `TzOnCalendar` is only ever created with a valid instance.
let result = unsafe {
let instance = instance.as_ptr();
((**instance).GetTimeZone)(instance, &mut string)
};
if result < 0 || string.is_null() {
Err(result)
} else {
Ok(Self {
string,
_header: None,
})
}
}
/// SAFETY: You are not allowed to release the returned pointer.
pub unsafe fn as_ptr(&self) -> HSTRING {
self.string
}
}
impl ToString for HString<'_> {
fn to_string(&self) -> String {
let mut len = 0;
// SAFETY: An `HString` is only ever created with a valid `HSTRING`.
// It keeps a reference to `HSTRING_HEADER` if needed.
let buf = unsafe { WindowsGetStringRawBuffer(self.string, &mut len) };
if len == 0 || buf.is_null() {
return String::new();
}
// SAFETY: `WindowsGetStringRawBuffer` returns a valid pointer to a wide string.
let slice = unsafe { std::slice::from_raw_parts(buf, len as usize) };
String::from_utf16_lossy(slice)
}
}
impl Drop for HString<'_> {
fn drop(&mut self) {
// SAFETY: An `HString` is only ever created with a valid `HSTRING`.
unsafe { WindowsDeleteString(self.string) };
}
}
}
mod instance {
use std::ptr::null_mut;
use windows_sys::core::HRESULT;
use windows_sys::Win32::System::WinRT::RoActivateInstance;
use super::hstring::HString;
use super::interfaces::IUnknown;
pub struct Instance(IUnknown);
impl Instance {
pub fn activate(class_id: &HString<'_>) -> Result<Self, HRESULT> {
let mut instance = null_mut();
// SAFETY: An `HString` is only ever crated with a valid `HSTRING`.
let result = unsafe { RoActivateInstance(class_id.as_ptr(), &mut instance) };
if result < 0 || instance.is_null() {
Err(result)
} else {
Ok(Self(instance.cast()))
}
}
/// SAFETY: You are not allowed to release the returned pointer.
pub unsafe fn as_ptr(&self) -> IUnknown {
self.0
}
}
impl Drop for Instance {
fn drop(&mut self) {
// SAFETY: An `Instance` is only ever created with a valid `IUnknown`.
unsafe { ((**self.0).Release)(self.0) };
}
}
}
mod tz_on_calendar {
use std::ptr::null_mut;
use windows_sys::core::HRESULT;
use super::instance::Instance;
use super::interfaces::{ITimeZoneOnCalendar, IUnknown};
use super::TIMEZONE_ON_CALENDAR_GUID;
pub struct TzOnCalendar(ITimeZoneOnCalendar);
impl TzOnCalendar {
pub fn query(source: &Instance) -> Result<Self, HRESULT> {
let mut tz = null_mut();
// SAFETY: An `Instance` is only ever created with a valid `IUnknown`.
let result = unsafe {
let source = source.as_ptr();
((**source).QueryInterface)(source, &TIMEZONE_ON_CALENDAR_GUID, &mut tz)
};
if result < 0 || tz.is_null() {
Err(result)
} else {
Ok(Self(tz.cast()))
}
}
/// SAFETY: You are not allowed to release the returned pointer.
pub unsafe fn as_ptr(&self) -> ITimeZoneOnCalendar {
self.0
}
}
impl Drop for TzOnCalendar {
fn drop(&mut self) {
let v: IUnknown = self.0.cast();
// SAFETY: `TzOnCalendar` is only ever created with a valid `ITimeZoneOnCalendar`.
unsafe { ((**v).Release)(v) };
}
}
}
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
mod interfaces {
use std::ops::Deref;
use windows_sys::core::{GUID, HRESULT, HSTRING};
pub type IUnknown = *mut *const IUnknown_Vtbl;
pub type IInspectable = *mut *const IInspectable_Vtbl;
pub type ITimeZoneOnCalendar = *mut *const ITimeZoneOnCalendar_Vtbl;
#[repr(C)]
pub struct IUnknown_Vtbl {
pub QueryInterface: unsafe extern "system" fn(
this: IUnknown,
iid: &GUID,
interface: &mut IUnknown,
) -> HRESULT,
pub AddRef: unsafe extern "system" fn(this: IUnknown) -> u32,
pub Release: unsafe extern "system" fn(this: IUnknown) -> u32,
}
#[repr(C)]
pub struct IInspectable_Vtbl {
pub base: IUnknown_Vtbl,
pub GetIids: unsafe extern "system" fn(
this: IInspectable,
count: &mut u32,
values: &mut &mut GUID,
) -> HRESULT,
pub GetRuntimeClassName:
unsafe extern "system" fn(this: IInspectable, value: &mut HSTRING) -> HRESULT,
pub GetTrustLevel:
unsafe extern "system" fn(this: IInspectable, value: &mut i32) -> HRESULT,
}
#[repr(C)]
pub struct ITimeZoneOnCalendar_Vtbl {
pub base: IInspectable_Vtbl,
pub GetTimeZone:
unsafe extern "system" fn(this: ITimeZoneOnCalendar, result: &mut HSTRING) -> HRESULT,
pub ChangeTimeZone:
unsafe extern "system" fn(this: ITimeZoneOnCalendar, timezoneid: HSTRING) -> HRESULT,
pub TimeZoneAsFullString:
unsafe extern "system" fn(this: ITimeZoneOnCalendar, result: *mut HSTRING) -> HRESULT,
pub TimeZoneAsString: unsafe extern "system" fn(
this: ITimeZoneOnCalendar,
ideallength: i32,
result: &mut HSTRING,
) -> HRESULT,
}
impl Deref for IInspectable_Vtbl {
type Target = IUnknown_Vtbl;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl Deref for ITimeZoneOnCalendar_Vtbl {
type Target = IInspectable_Vtbl;
fn deref(&self) -> &Self::Target {
&self.base
}
}
}