From d9b268369d345076d8cf63ee83c54c3ef5f4206c Mon Sep 17 00:00:00 2001 From: Ashley Stanton-Nurse Date: Wed, 11 Sep 2024 16:31:09 -0700 Subject: [PATCH] pr feedback --- .github/CODEOWNERS | 2 +- .vscode/cspell.json | 15 ++++--- Cargo.toml | 2 +- .../azure_data_cosmos/Cargo.toml | 7 ++- .../azure_data_cosmos/README.md | 0 .../examples/cosmos_connect.rs} | 12 ++--- .../src/authorization_policy.rs | 32 +++++++------- .../src/clients/cosmos_client.rs | 19 +++++--- .../src/clients/database_client.rs | 15 ++++--- .../azure_data_cosmos/src/clients/mod.rs | 0 .../azure_data_cosmos/src/lib.rs | 4 +- .../azure_data_cosmos/src/models/mod.rs | 13 +++--- .../azure_data_cosmos/src/options/mod.rs | 44 ++++++++++++++++++- sdk/cosmosdb/cosmosdb.dict.txt | 4 -- .../typespec_client_core/src/date/mod.rs | 2 +- 15 files changed, 109 insertions(+), 62 deletions(-) rename sdk/{cosmosdb => cosmos}/azure_data_cosmos/Cargo.toml (85%) rename sdk/{cosmosdb => cosmos}/azure_data_cosmos/README.md (100%) rename sdk/{cosmosdb/azure_data_cosmos/examples/cosmosdb_connect.rs => cosmos/azure_data_cosmos/examples/cosmos_connect.rs} (80%) rename sdk/{cosmosdb => cosmos}/azure_data_cosmos/src/authorization_policy.rs (96%) rename sdk/{cosmosdb => cosmos}/azure_data_cosmos/src/clients/cosmos_client.rs (88%) rename sdk/{cosmosdb => cosmos}/azure_data_cosmos/src/clients/database_client.rs (83%) rename sdk/{cosmosdb => cosmos}/azure_data_cosmos/src/clients/mod.rs (100%) rename sdk/{cosmosdb => cosmos}/azure_data_cosmos/src/lib.rs (91%) rename sdk/{cosmosdb => cosmos}/azure_data_cosmos/src/models/mod.rs (84%) rename sdk/{cosmosdb => cosmos}/azure_data_cosmos/src/options/mod.rs (66%) delete mode 100644 sdk/cosmosdb/cosmosdb.dict.txt diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 89b5d741fd..eb934b5d57 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -35,7 +35,7 @@ # ServiceOwner: @Pilchie # ServiceLabel: %Cosmos # PRLabel: %Cosmos -/sdk/cosmosdb/ @analogrelay @Pilchie +/sdk/cosmos/ @analogrelay @Pilchie ########### # Eng Sys diff --git a/.vscode/cspell.json b/.vscode/cspell.json index 8e2671b802..9f50cf5917 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -56,8 +56,8 @@ "noSuggest": true }, { - "name": "cosmosdb", - "path": "../sdk/cosmosdb/cosmosdb.dict.txt", + "name": "cosmos", + "path": "../sdk/cosmos/.dict.txt", "noSuggest": true } ], @@ -103,11 +103,12 @@ ] }, { - "filename": "sdk/cosmosdb/azure_data_cosmos/**", - "dictionaries": [ - "crates", - "cosmosdb", - "rust-custom" + "filename": "sdk/cosmos/**", + "words": [ + "colls", + "pkranges", + "sprocs", + "udfs", ] } ] diff --git a/Cargo.toml b/Cargo.toml index d06b6ce3f5..2eb2805844 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ "sdk/typespec/typespec_derive", "sdk/core/azure_core", "sdk/core/azure_core_amqp", - "sdk/cosmosdb/azure_data_cosmos", + "sdk/cosmos/azure_data_cosmos", "sdk/identity/azure_identity", "sdk/eventhubs/azure_messaging_eventhubs", "eng/test/mock_transport", diff --git a/sdk/cosmosdb/azure_data_cosmos/Cargo.toml b/sdk/cosmos/azure_data_cosmos/Cargo.toml similarity index 85% rename from sdk/cosmosdb/azure_data_cosmos/Cargo.toml rename to sdk/cosmos/azure_data_cosmos/Cargo.toml index 0a4c8033f9..a9d6f2565b 100644 --- a/sdk/cosmosdb/azure_data_cosmos/Cargo.toml +++ b/sdk/cosmos/azure_data_cosmos/Cargo.toml @@ -10,7 +10,7 @@ repository.workspace = true rust-version.workspace = true homepage = "https://github.com/azure/azure-sdk-for-rust" documentation = "https://docs.rs/azure_data_cosmos" -keywords = ["sdk", "azure", "rest", "cloud", "cosmosdb", "database"] +keywords = ["sdk", "azure", "rest", "cloud", "cosmos", "database"] categories = ["api-bindings"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -19,7 +19,6 @@ categories = ["api-bindings"] async-trait.workspace = true azure_core.workspace = true typespec_client_core = { workspace = true, features = ["derive"] } -time.workspace = true tracing.workspace = true url.workspace = true serde.workspace = true @@ -37,7 +36,7 @@ workspace = true default = ["hmac_rust"] hmac_rust = ["azure_core/hmac_rust"] hmac_openssl = ["azure_core/hmac_openssl"] -key-auth = [] # Enables support for key-based authentication (Primary Keys and Resource Tokens) +key_auth = [] # Enables support for key-based authentication (Primary Keys and Resource Tokens) [package.metadata.docs.rs] -features = ["key-auth"] +features = ["key_auth"] diff --git a/sdk/cosmosdb/azure_data_cosmos/README.md b/sdk/cosmos/azure_data_cosmos/README.md similarity index 100% rename from sdk/cosmosdb/azure_data_cosmos/README.md rename to sdk/cosmos/azure_data_cosmos/README.md diff --git a/sdk/cosmosdb/azure_data_cosmos/examples/cosmosdb_connect.rs b/sdk/cosmos/azure_data_cosmos/examples/cosmos_connect.rs similarity index 80% rename from sdk/cosmosdb/azure_data_cosmos/examples/cosmosdb_connect.rs rename to sdk/cosmos/azure_data_cosmos/examples/cosmos_connect.rs index dfbfe625a5..4e785d5116 100644 --- a/sdk/cosmosdb/azure_data_cosmos/examples/cosmosdb_connect.rs +++ b/sdk/cosmos/azure_data_cosmos/examples/cosmos_connect.rs @@ -1,10 +1,10 @@ -use azure_data_cosmos::{CosmosClient, CosmosClientMethods, DatabaseClientMethods}; +use azure_data_cosmos::{clients::DatabaseClientMethods, CosmosClient, CosmosClientMethods}; use clap::Parser; /// A simple example to show connecting to a Cosmos Account and retrieving the properties of a database. #[derive(Parser)] pub struct Args { - /// The CosmosDB endpoint to connect to. + /// The Cosmos endpoint to connect to. endpoint: String, /// The database to fetch information for. @@ -12,7 +12,7 @@ pub struct Args { /// An authentication key to use when connecting to the Cosmos DB account. If omitted, the connection will use Entra ID. #[clap(long)] - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] key: Option, } @@ -22,13 +22,13 @@ pub async fn main() -> Result<(), Box> { let client = create_client(&args); - let db_client = client.database(&args.database); + let db_client = client.database_client(&args.database); let response = db_client.read(None).await?.deserialize_body().await?; println!("{:?}", response); Ok(()) } -#[cfg(feature = "key-auth")] +#[cfg(feature = "key_auth")] fn create_client(args: &Args) -> CosmosClient { if let Some(key) = args.key.as_ref() { CosmosClient::with_key(&args.endpoint, key.clone(), None).unwrap() @@ -38,7 +38,7 @@ fn create_client(args: &Args) -> CosmosClient { } } -#[cfg(not(feature = "key-auth"))] +#[cfg(not(feature = "key_auth"))] fn create_client(args: &Args) -> CosmosClient { let cred = azure_identity::create_default_credential().unwrap(); CosmosClient::new(&args.endpoint, cred, None).unwrap() diff --git a/sdk/cosmosdb/azure_data_cosmos/src/authorization_policy.rs b/sdk/cosmos/azure_data_cosmos/src/authorization_policy.rs similarity index 96% rename from sdk/cosmosdb/azure_data_cosmos/src/authorization_policy.rs rename to sdk/cosmos/azure_data_cosmos/src/authorization_policy.rs index b089e29bf2..bc3ca42440 100644 --- a/sdk/cosmosdb/azure_data_cosmos/src/authorization_policy.rs +++ b/sdk/cosmos/azure_data_cosmos/src/authorization_policy.rs @@ -5,17 +5,17 @@ //! We implement that policy here, because we can't use any standard Azure SDK authentication policy. use azure_core::auth::TokenCredential; +use azure_core::date::OffsetDateTime; use azure_core::{ date, headers::{HeaderValue, AUTHORIZATION, MS_DATE, VERSION}, Context, Policy, PolicyResult, Request, Url, }; use std::sync::Arc; -use time::OffsetDateTime; use tracing::trace; use url::form_urlencoded; -#[cfg(feature = "key-auth")] +#[cfg(feature = "key_auth")] use azure_core::{auth::Secret, hmac::hmac_sha256}; const AZURE_VERSION: &str = "2018-12-31"; @@ -42,11 +42,11 @@ enum Credential { Token(Arc), /// The credential is a key to be used to sign the HTTP request (a shared key) - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] PrimaryKey(Secret), } -#[cfg(feature = "key-auth")] +#[cfg(feature = "key_auth")] struct SignatureTarget<'a> { http_method: &'a azure_core::Method, resource_type: &'a ResourceType, @@ -66,7 +66,7 @@ impl AuthorizationPolicy { } } - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] pub(crate) fn from_shared_key(key: Secret) -> Self { Self { credential: Credential::PrimaryKey(key), @@ -99,7 +99,7 @@ impl Policy for AuthorizationPolicy { let auth = generate_authorization( &self.credential, request.url(), - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] SignatureTarget { http_method: request.method(), resource_type, @@ -186,7 +186,7 @@ fn extract_resource_link(request: &Request) -> String { async fn generate_authorization<'a>( auth_token: &Credential, url: &Url, - #[cfg(feature = "key-auth")] signature_target: SignatureTarget<'a>, + #[cfg(feature = "key_auth")] signature_target: SignatureTarget<'a>, ) -> azure_core::Result { let (authorization_type, signature) = match auth_token { Credential::Token(token_credential) => ( @@ -199,7 +199,7 @@ async fn generate_authorization<'a>( .to_string(), ), - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] Credential::PrimaryKey(key) => { let string_to_sign = string_to_sign(signature_target); ("master", hmac_sha256(&string_to_sign, key)?) @@ -222,7 +222,7 @@ fn scope_from_url(url: &Url) -> String { /// This function generates a valid authorization string, according to the documentation. /// In case of authorization problems we can compare the `string_to_sign` generated by Azure against /// our own. -#[cfg(feature = "key-auth")] +#[cfg(feature = "key_auth")] fn string_to_sign(signature_target: SignatureTarget) -> String { // From official docs: // StringToSign = @@ -266,16 +266,16 @@ fn string_to_sign(signature_target: SignatureTarget) -> String { #[cfg(test)] mod tests { - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] use azure_core::auth::AccessToken; use super::*; #[derive(Debug)] - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] struct TestTokenCredential(String); - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] impl TokenCredential for TestTokenCredential { @@ -293,7 +293,7 @@ mod tests { } #[test] - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] fn string_to_sign_generates_expected_string_for_signing() { let time_nonce = date::parse_rfc3339("1900-01-01T01:00:00.000000000+00:00").unwrap(); @@ -315,7 +315,7 @@ mon, 01 jan 1900 01:00:00 gmt } #[tokio::test] - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] async fn generate_authorization_for_token_credential() { let time_nonce = date::parse_rfc3339("1900-01-01T01:00:00.000000000+00:00").unwrap(); let cred = Arc::new(TestTokenCredential("test_token".to_string())); @@ -346,7 +346,7 @@ mon, 01 jan 1900 01:00:00 gmt } #[tokio::test] - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] async fn generate_authorization_for_primary_key_0() { let time_nonce = date::parse_rfc3339("1900-01-01T01:00:00.000000000+00:00").unwrap(); @@ -379,7 +379,7 @@ mon, 01 jan 1900 01:00:00 gmt } #[tokio::test] - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] async fn generate_authorization_for_primary_key_1() { let time_nonce = date::parse_rfc3339("2017-04-27T00:51:12.000000000+00:00").unwrap(); diff --git a/sdk/cosmosdb/azure_data_cosmos/src/clients/cosmos_client.rs b/sdk/cosmos/azure_data_cosmos/src/clients/cosmos_client.rs similarity index 88% rename from sdk/cosmosdb/azure_data_cosmos/src/clients/cosmos_client.rs rename to sdk/cosmos/azure_data_cosmos/src/clients/cosmos_client.rs index 0195c1ba7c..d54032f504 100644 --- a/sdk/cosmosdb/azure_data_cosmos/src/clients/cosmos_client.rs +++ b/sdk/cosmos/azure_data_cosmos/src/clients/cosmos_client.rs @@ -1,10 +1,11 @@ use crate::authorization_policy::AuthorizationPolicy; -use crate::{CosmosClientOptions, DatabaseClient}; +use crate::clients::DatabaseClient; +use crate::CosmosClientOptions; use azure_core::auth::TokenCredential; use azure_core::{Pipeline, Url}; use std::sync::Arc; -#[cfg(feature = "key-auth")] +#[cfg(feature = "key_auth")] use azure_core::auth::Secret; /// Client for Azure Cosmos DB. @@ -23,7 +24,10 @@ pub struct CosmosClient { /// Rather than depending on `CosmosClient`, you can depend on a generic parameter constrained by this trait, or an `impl CosmosClientMethods` type. pub trait CosmosClientMethods { /// Gets a [`DatabaseClient`] that can be used to access the database with the specified ID. - fn database(&self, name: impl Into) -> DatabaseClient; + /// + /// # Arguments + /// * `id` - The ID of the database. + fn database_client(&self, id: impl AsRef) -> DatabaseClient; } impl CosmosClient { @@ -72,7 +76,7 @@ impl CosmosClient { /// ```rust,no_run /// let client = CosmosClient::with_shared_key("https://myaccount.documents.azure.com/", "my_key", None)?; /// ``` - #[cfg(feature = "key-auth")] + #[cfg(feature = "key_auth")] pub fn with_key( endpoint: impl AsRef, key: impl Into, @@ -97,8 +101,11 @@ impl CosmosClient { impl CosmosClientMethods for CosmosClient { /// Gets a [`DatabaseClient`] that can be used to access the database with the specified ID. - fn database(&self, id: impl Into) -> DatabaseClient { - DatabaseClient::new(self.clone(), id.into()) + /// + /// # Arguments + /// * `id` - The ID of the database. + fn database_client(&self, id: impl AsRef) -> DatabaseClient { + DatabaseClient::new(self.clone(), id.as_ref()) } } diff --git a/sdk/cosmosdb/azure_data_cosmos/src/clients/database_client.rs b/sdk/cosmos/azure_data_cosmos/src/clients/database_client.rs similarity index 83% rename from sdk/cosmosdb/azure_data_cosmos/src/clients/database_client.rs rename to sdk/cosmos/azure_data_cosmos/src/clients/database_client.rs index b2ec67bf74..c82cf10c5e 100644 --- a/sdk/cosmosdb/azure_data_cosmos/src/clients/database_client.rs +++ b/sdk/cosmos/azure_data_cosmos/src/clients/database_client.rs @@ -22,33 +22,34 @@ pub trait DatabaseClientMethods { /// /// ```rust,no_run /// # async fn doc() { - /// use azure_data_cosmos::{CosmosClient, CosmosClientMethods, DatabaseClientMethods}; + /// use azure_data_cosmos::{CosmosClient, CosmosClientMethods, clients::DatabaseClientMethods}; /// /// let credential = azure_identity::create_default_credential().unwrap(); /// let client = CosmosClient::new("https://myaccount.documents.azure.com/", credential, None).unwrap(); - /// let db_client = client.database("my_database"); + /// let db_client = client.database_client("my_database"); /// let response = db_client.read(None) /// .await.unwrap() /// .deserialize_body() /// .await.unwrap(); /// # } /// ``` - fn read( + #[allow(async_fn_in_trait)] // REASON: See https://github.com/Azure/azure-sdk-for-rust/issues/1796 for detailed justification + async fn read( &self, options: Option, - ) -> impl std::future::Future>>; + ) -> azure_core::Result>; } /// A client for working with a specific database in a Cosmos account. /// -/// You can get a `DatabaseClient` by calling [`CosmosClient::database()`](CosmosClient::database). +/// You can get a `DatabaseClient` by calling [`CosmosClient::database_client()`](CosmosClient::database_client()). pub struct DatabaseClient { base_url: Url, root_client: CosmosClient, } impl DatabaseClient { - pub(crate) fn new(root_client: CosmosClient, database_id: String) -> Self { + pub(crate) fn new(root_client: CosmosClient, database_id: &str) -> Self { let base_url = { let mut u = root_client.endpoint().clone(); { @@ -56,7 +57,7 @@ impl DatabaseClient { .path_segments_mut() .expect("The root client should have validated the format of the URL"); segments.push("dbs"); - segments.push(&database_id); + segments.push(database_id); } u }; diff --git a/sdk/cosmosdb/azure_data_cosmos/src/clients/mod.rs b/sdk/cosmos/azure_data_cosmos/src/clients/mod.rs similarity index 100% rename from sdk/cosmosdb/azure_data_cosmos/src/clients/mod.rs rename to sdk/cosmos/azure_data_cosmos/src/clients/mod.rs diff --git a/sdk/cosmosdb/azure_data_cosmos/src/lib.rs b/sdk/cosmos/azure_data_cosmos/src/lib.rs similarity index 91% rename from sdk/cosmosdb/azure_data_cosmos/src/lib.rs rename to sdk/cosmos/azure_data_cosmos/src/lib.rs index abe9baf788..7347baf8fc 100644 --- a/sdk/cosmosdb/azure_data_cosmos/src/lib.rs +++ b/sdk/cosmos/azure_data_cosmos/src/lib.rs @@ -8,11 +8,11 @@ #![cfg_attr(docsrs, feature(doc_cfg_hide))] mod authorization_policy; -mod clients; +pub mod clients; mod options; /// Model types sent to and received from the Cosmos API. pub mod models; -pub use clients::*; +pub use clients::{CosmosClient, CosmosClientMethods}; pub use options::*; diff --git a/sdk/cosmosdb/azure_data_cosmos/src/models/mod.rs b/sdk/cosmos/azure_data_cosmos/src/models/mod.rs similarity index 84% rename from sdk/cosmosdb/azure_data_cosmos/src/models/mod.rs rename to sdk/cosmos/azure_data_cosmos/src/models/mod.rs index 8563033c47..eae800075e 100644 --- a/sdk/cosmosdb/azure_data_cosmos/src/models/mod.rs +++ b/sdk/cosmos/azure_data_cosmos/src/models/mod.rs @@ -1,15 +1,16 @@ -use azure_core::Model; +use azure_core::{ + date::{ComponentRange, OffsetDateTime}, + Model, +}; use serde::{Deserialize, Serialize}; -use time::error::ComponentRange; -use time::OffsetDateTime; #[cfg(doc)] -use crate::DatabaseClientMethods; +use crate::{clients::DatabaseClientMethods, CosmosClientMethods}; /// Represents a timestamp in the format expected by Cosmos DB. /// /// Cosmos DB timestamps are represented as the number of seconds since the Unix epoch. -/// Use [`CosmosTimestamp::try_into`] implementation to convert this into a [`time::OffsetDateTime`]. +/// Use [`CosmosTimestamp::try_into`] implementation to convert this into an [`OffsetDateTime`]. #[derive(Serialize, Deserialize, Debug)] pub struct CosmosTimestamp(i64); @@ -25,7 +26,7 @@ impl TryInto for CosmosTimestamp { /// Properties of a Cosmos DB database. /// -/// Returned by [`DatabaseClient::read()`](crate::DatabaseClient::read()). +/// Returned by [`DatabaseClient::read()`](crate::clients::DatabaseClient::read()). #[derive(Model, Debug, Deserialize)] pub struct DatabaseProperties { /// The ID of the database. diff --git a/sdk/cosmosdb/azure_data_cosmos/src/options/mod.rs b/sdk/cosmos/azure_data_cosmos/src/options/mod.rs similarity index 66% rename from sdk/cosmosdb/azure_data_cosmos/src/options/mod.rs rename to sdk/cosmos/azure_data_cosmos/src/options/mod.rs index b312a11afc..addb9c655e 100644 --- a/sdk/cosmosdb/azure_data_cosmos/src/options/mod.rs +++ b/sdk/cosmos/azure_data_cosmos/src/options/mod.rs @@ -22,7 +22,7 @@ impl CosmosClientOptions { } } -/// Options to be passed to [`DatabaseClientMethods::read()`](crate::DatabaseClientMethods::read()). +/// Options to be passed to [`DatabaseClientMethods::read()`](crate::clients::DatabaseClientMethods::read()). /// /// NOTE: There are currently no options to set on this type. /// It exists to enable future extensibility. @@ -44,6 +44,8 @@ impl ReadDatabaseOptions { /// Builders for Cosmos-related options structs. pub mod builders { + use azure_core::builders::ClientOptionsBuilder; + use crate::{CosmosClientOptions, ReadDatabaseOptions}; /// Builder used to construct a [`CosmosClientOptions`]. @@ -59,6 +61,46 @@ pub mod builders { } } + impl ClientOptionsBuilder for CosmosClientOptionsBuilder { + fn with_per_call_policies

(mut self, per_call_policies: P) -> Self + where + P: Into>>, + Self: Sized, + { + self.0 + .client_options + .set_per_call_policies(per_call_policies); + self + } + + fn with_per_try_policies

(mut self, per_try_policies: P) -> Self + where + P: Into>>, + Self: Sized, + { + self.0.client_options.set_per_try_policies(per_try_policies); + self + } + + fn with_retry

(mut self, retry: P) -> Self + where + P: Into, + Self: Sized, + { + self.0.client_options.set_retry(retry); + self + } + + fn with_transport

(mut self, transport: P) -> Self + where + P: Into, + Self: Sized, + { + self.0.client_options.set_transport(transport); + self + } + } + /// Builder used to construct a [`ReadDatabaseOptions`]. #[derive(Default)] pub struct ReadDatabaseOptionsBuilder(ReadDatabaseOptions); diff --git a/sdk/cosmosdb/cosmosdb.dict.txt b/sdk/cosmosdb/cosmosdb.dict.txt deleted file mode 100644 index 9dbfbcf32e..0000000000 --- a/sdk/cosmosdb/cosmosdb.dict.txt +++ /dev/null @@ -1,4 +0,0 @@ -colls -pkranges -sprocs -udfs diff --git a/sdk/typespec/typespec_client_core/src/date/mod.rs b/sdk/typespec/typespec_client_core/src/date/mod.rs index fc4e19d863..7506b746da 100644 --- a/sdk/typespec/typespec_client_core/src/date/mod.rs +++ b/sdk/typespec/typespec_client_core/src/date/mod.rs @@ -7,7 +7,7 @@ // use std::time::Duration; -pub use time::OffsetDateTime; +pub use time::{error::ComponentRange, OffsetDateTime}; use time::{ format_description::{well_known::Rfc3339, FormatItem}, macros::format_description,