|
| 1 | +// Copyright 2020-2024 IOTA Stiftung |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +//! An implementation of `now_utc` which calls out to an externally defined function. |
| 5 | +use crate::common::Timestamp; |
| 6 | + |
| 7 | +/// Register a function to be invoked by `identity_core` in order to get a [Timestamp] representing |
| 8 | +/// "now". |
| 9 | +/// |
| 10 | +/// ## Writing a custom `now_utc` implementation |
| 11 | +/// |
| 12 | +/// The function to register must have the same signature as |
| 13 | +/// [`Timestamp::now_utc`](Timestamp::now_utc). The function can be defined |
| 14 | +/// wherever you want, either in root crate or a dependent crate. |
| 15 | +/// |
| 16 | +/// For example, if we wanted a `static_now_utc` crate containing an |
| 17 | +/// implementation that always returns the same timestamp, we would first depend on `identity_core` |
| 18 | +/// (for the [`Timestamp`] type) in `static_now_utc/Cargo.toml`: |
| 19 | +/// ```toml |
| 20 | +/// [dependencies] |
| 21 | +/// identity_core = "1" |
| 22 | +/// ``` |
| 23 | +/// Note that the crate containing this function does **not** need to enable the |
| 24 | +/// `"custom_time"` Cargo feature. |
| 25 | +/// |
| 26 | +/// Next, in `static_now_utc/src/lib.rs`, we define our function: |
| 27 | +/// ```rust |
| 28 | +/// use identity_core::common::Timestamp; |
| 29 | +/// |
| 30 | +/// // Some fixed timestamp |
| 31 | +/// const MY_FIXED_TIMESTAMP: i64 = 1724402964; |
| 32 | +/// pub fn static_now_utc() -> Timestamp { |
| 33 | +/// Timestamp::from_unix(MY_FIXED_TIMESTAMP).unwrap() |
| 34 | +/// } |
| 35 | +/// ``` |
| 36 | +/// |
| 37 | +/// ## Registering a custom `now_utc` implementation |
| 38 | +/// |
| 39 | +/// Functions can only be registered in the root binary crate. Attempting to |
| 40 | +/// register a function in a non-root crate will result in a linker error. |
| 41 | +/// This is similar to |
| 42 | +/// [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) or |
| 43 | +/// [`#[global_allocator]`](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/global-allocators.html), |
| 44 | +/// where helper crates define handlers/allocators but only the binary crate |
| 45 | +/// actually _uses_ the functionality. |
| 46 | +/// |
| 47 | +/// To register the function, we first depend on `static_now_utc` _and_ |
| 48 | +/// `identity_core` in `Cargo.toml`: |
| 49 | +/// ```toml |
| 50 | +/// [dependencies] |
| 51 | +/// static_now_utc = "0.1" |
| 52 | +/// identity_core = { version = "1", features = ["custom_time"] } |
| 53 | +/// ``` |
| 54 | +/// |
| 55 | +/// Then, we register the function in `src/main.rs`: |
| 56 | +/// ```rust |
| 57 | +/// # mod static_now_utc { pub fn static_now_utc() -> Timestamp { unimplemented!() } } |
| 58 | +/// |
| 59 | +/// use identity_core::register_custom_now_utc; |
| 60 | +/// use static_now_utc::static_now_utc; |
| 61 | +/// |
| 62 | +/// register_custom_now_utc!(static_now_utc); |
| 63 | +/// ``` |
| 64 | +/// |
| 65 | +/// Now any user of `now_utc` (direct or indirect) on this target will use the |
| 66 | +/// registered function. |
| 67 | +#[macro_export] |
| 68 | +macro_rules! register_custom_now_utc { |
| 69 | + ($path:path) => { |
| 70 | + const __GET_TIME_INTERNAL: () = { |
| 71 | + // We use Rust ABI to be safe against potential panics in the passed function. |
| 72 | + #[no_mangle] |
| 73 | + unsafe fn __now_utc_custom() -> Timestamp { |
| 74 | + // Make sure the passed function has the type of `now_utc_custom` |
| 75 | + type F = fn() -> Timestamp; |
| 76 | + let f: F = $path; |
| 77 | + f() |
| 78 | + } |
| 79 | + }; |
| 80 | + }; |
| 81 | +} |
| 82 | + |
| 83 | +pub(crate) fn now_utc_custom() -> Timestamp { |
| 84 | + extern "Rust" { |
| 85 | + fn __now_utc_custom() -> Timestamp; |
| 86 | + } |
| 87 | + unsafe { __now_utc_custom() } |
| 88 | +} |
0 commit comments