diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..5e4c2e8 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,4 @@ +max_width = 80 +reorder_imports = true +group_imports = "StdExternalCrate" +imports_granularity = "Module" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index f781e42..2f0b45c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,9 +100,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cgp" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "255ecfdb81d1786bf4212e1e038e5f6da8836cbb5603ce2347308ede426dbcea" +checksum = "d3d555a86484a22b3233de8248b980a482d8f92190e4823061a5d41250b74537" dependencies = [ "cgp-async", "cgp-core", @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "cgp-async" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cca3a368634ced2b77f1e00e7ebb0872402a1d8fd6dbf05880329b0a001b37e" +checksum = "5eb185d91812fb0cf811ef592279010c817462c0c722562b09e987bef800e432" dependencies = [ "cgp-async-macro", "cgp-sync", @@ -121,9 +121,9 @@ dependencies = [ [[package]] name = "cgp-async-macro" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1656912abbbf7ff2fe11c9680ae5883afe79cac6f2f51bd486d04a3fbba9426" +checksum = "0620dfb16be4b6087562ba9b38f67a67ab7a848cde2eb32241f3f606afe3560a" dependencies = [ "proc-macro2", "quote", @@ -132,65 +132,41 @@ dependencies = [ [[package]] name = "cgp-component" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12793289566a983f8a3151448cb8820bcc4704c34a0a9cf19bb1ed48e0844e81" - -[[package]] -name = "cgp-component-macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f37331b62cbb29d159d2c3bc3c21bdc885576d9c024d0e9ea4cb3e7513138897" -dependencies = [ - "cgp-component-macro-lib", - "syn", -] - -[[package]] -name = "cgp-component-macro-lib" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29461124d295bf42c48106e81cb0f89a4eb64132752b916d5ec5df9875d03d58" -dependencies = [ - "itertools", - "prettyplease", - "proc-macro2", - "quote", - "syn", -] +checksum = "674d048a0c12498626259d710899dad116d47a90498259209c9f7d412ce254b4" [[package]] name = "cgp-core" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021293135043b9a4f630a1fea79c1cee2de5161e71902e2cd665965945d4b546" +checksum = "3cf2a22653dca218ae625f611223a5e0b27f54285f6d075fc7d7a96a942dfd64" dependencies = [ "cgp-async", "cgp-component", - "cgp-component-macro", "cgp-error", "cgp-field", - "cgp-field-macro", + "cgp-macro", "cgp-type", ] [[package]] name = "cgp-error" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4b69e873357e304d1a7a91894f40f98543a5f1fac84b3087037110826e4afe" +checksum = "e754473b5da401e55bede80efda8b6da07b78acac87c284a492a350156d6e6a1" dependencies = [ "cgp-async", "cgp-component", - "cgp-component-macro", + "cgp-macro", "cgp-type", ] [[package]] name = "cgp-error-anyhow" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e69c88a7ec1ce29d204d6f9d3b975933a050dc1b1b71da646fc74e56f0cdbe7" +checksum = "b9dd2600d79e7607f6bae56057a44884e2ee672ab87aee14d4a8bbdda96fd58e" dependencies = [ "anyhow", "cgp-core", @@ -198,18 +174,18 @@ dependencies = [ [[package]] name = "cgp-error-extra" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb44c5e45fb8de9d5e8d77a4736c01d09fdee2f4e822b5ea5a3b6243ddbdd9c" +checksum = "d19650e17295e4a1aec5b2d3bd35cee8a98f1dc138715d46fbfe50eeaeab5ff4" dependencies = [ - "cgp-error", + "cgp-core", ] [[package]] name = "cgp-extra" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04a67b3e5c5fd4420c0fd16c1e93a9e28a7e10f9aa911b66d7a2b9588fbb11c4" +checksum = "21fa7c3e40fdb7d1b21331e33b38262504449146e23b370f1533c80ea3ecaf52" dependencies = [ "cgp-error-extra", "cgp-inner", @@ -219,44 +195,46 @@ dependencies = [ [[package]] name = "cgp-field" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193dec7508c4b4efd6a1ef5eac6789eb0a297561e55dcb8cd377a904f055a176" +checksum = "7f585da1ae8aa7182cce2474329a324df18b75bea738113295ba8048af8bee9f" dependencies = [ "cgp-component", + "cgp-macro", "cgp-type", ] [[package]] -name = "cgp-field-macro" -version = "0.3.0" +name = "cgp-inner" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7853451aedcc73c0edf67d44ca9652a7b1ba8a18ac0499f4fced1ac845b7711" +checksum = "d8e9ad8fc246dd919e514a889704aea9a82093f42e7877eed03a2afe964c884b" dependencies = [ - "cgp-field-macro-lib", - "proc-macro2", + "cgp-component", + "cgp-macro", ] [[package]] -name = "cgp-field-macro-lib" -version = "0.3.0" +name = "cgp-macro" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "785d154b6d17929919e3bee306c515c7d78e420303605829e48116fcef2ccf9e" +checksum = "e4df8aef8d3b32725b6b088bb41b6f96a212f945b9993f78760b79ba038e0194" dependencies = [ - "prettyplease", - "proc-macro2", - "quote", + "cgp-macro-lib", "syn", ] [[package]] -name = "cgp-inner" -version = "0.3.0" +name = "cgp-macro-lib" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4e35f1ad7bfb5df01c9f263d054ba53ff87c3d1da20cb5c0872191df904583" +checksum = "7bc6d7d6235b0673071150bd45b01818ca8402e0b005936d3d8f14170a0a71e5" dependencies = [ - "cgp-component", - "cgp-component-macro", + "itertools", + "prettyplease", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -276,42 +254,42 @@ dependencies = [ [[package]] name = "cgp-run" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec7009079ecfd0a5db07a5dda0e7cece70b1774ead842a42919a93b4662eb96" +checksum = "309ebfa7a9d0190f82c75b59c59d651c158473f9613289bb26950fbca39278ab" dependencies = [ "cgp-async", "cgp-component", - "cgp-component-macro", "cgp-error", + "cgp-macro", ] [[package]] name = "cgp-runtime" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332d92088b07754adbde9ecdf894b8c3b9303b287c8d82d11f8886a394121925" +checksum = "76183a413398fab9230fd6f7ebf17411bf4e20e787c5b01ca3cb02309ec6873b" dependencies = [ "cgp-core", ] [[package]] name = "cgp-sync" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb37ca18a65f3603aaa307ba76d92f64025db56160143a0e96466b5855d61a29" +checksum = "15a52ca0d8647252e4a07a72a63364ee56353deeaa98926b2f5e7d65ab3c385c" dependencies = [ "cgp-async-macro", ] [[package]] name = "cgp-type" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a4638c888a38fbb1c88406c224c38d7b9e230a306e7b07f796280b89f5baa54" +checksum = "7aabb2927728ed5b4b4e76ba3e054d9d772af64dd0140d4748aa766f7dbad914" dependencies = [ "cgp-component", - "cgp-component-macro", + "cgp-macro", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 20945d3..1069dd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -cgp = { version = "0.3.0" } -cgp-error-anyhow = { version = "0.3.0" } +cgp = { version = "0.4.0" } +cgp-error-anyhow = { version = "0.4.0" } serde = { version = "1", features = ["derive"] } reqwest = { version = "0.12.12", features = [ "blocking", "json" ] } itertools = { version = "0.14.0" } @@ -21,12 +21,10 @@ sha1 = { version = "0.10.6" } # cgp-async = { git = "https://github.com/contextgeneric/cgp.git" } # cgp-async-macro = { git = "https://github.com/contextgeneric/cgp.git" } # cgp-component = { git = "https://github.com/contextgeneric/cgp.git" } -# cgp-component-macro = { git = "https://github.com/contextgeneric/cgp.git" } -# cgp-component-macro-lib = { git = "https://github.com/contextgeneric/cgp.git" } +# cgp-macro = { git = "https://github.com/contextgeneric/cgp.git" } +# cgp-macro-lib = { git = "https://github.com/contextgeneric/cgp.git" } # cgp-type = { git = "https://github.com/contextgeneric/cgp.git" } # cgp-field = { git = "https://github.com/contextgeneric/cgp.git" } -# cgp-field-macro = { git = "https://github.com/contextgeneric/cgp.git" } -# cgp-field-macro-lib = { git = "https://github.com/contextgeneric/cgp.git" } # cgp-error = { git = "https://github.com/contextgeneric/cgp.git" } # cgp-error-extra = { git = "https://github.com/contextgeneric/cgp.git" } # cgp-error-anyhow = { git = "https://github.com/contextgeneric/cgp.git" } diff --git a/content/SUMMARY.md b/content/SUMMARY.md index 6a01cb9..f0855e6 100644 --- a/content/SUMMARY.md +++ b/content/SUMMARY.md @@ -15,8 +15,8 @@ - [Provider Traits](provider-traits.md) - [Linking Consumers with Providers](consumer-provider-link.md) - [Provider Delegation](provider-delegation.md) +- [Debugging Support](debugging-support.md) - [Component Macros](component-macros.md) -- [Debugging Techniques](debugging-techniques.md) # Design Patterns diff --git a/content/associated-types.md b/content/associated-types.md index f0a068e..7cffbb1 100644 --- a/content/associated-types.md +++ b/content/associated-types.md @@ -15,23 +15,17 @@ pub mod traits { use anyhow::Error; use cgp::prelude::*; - #[cgp_component { - provider: AuthTokenValidator, - }] + #[cgp_component(AuthTokenValidator)] pub trait CanValidateAuthToken { fn validate_auth_token(&self, auth_token: &str) -> Result<(), Error>; } - #[cgp_component { - provider: AuthTokenExpiryFetcher, - }] + #[cgp_component(AuthTokenExpiryFetcher)] pub trait CanFetchAuthTokenExpiry { fn fetch_auth_token_expiry(&self, auth_token: &str) -> Result; } - #[cgp_component { - provider: CurrentTimeGetter, - }] + #[cgp_component(CurrentTimeGetter)] pub trait HasCurrentTime { fn current_time(&self) -> Result; } @@ -41,11 +35,11 @@ pub mod impls { use std::time::{SystemTime, UNIX_EPOCH}; use anyhow::{anyhow, Error}; + use cgp::prelude::*; use super::traits::*; - pub struct ValidateTokenIsNotExpired; - + #[cgp_new_provider] impl AuthTokenValidator for ValidateTokenIsNotExpired where Context: HasCurrentTime + CanFetchAuthTokenExpiry, @@ -63,8 +57,7 @@ pub mod impls { } } - pub struct GetSystemTimestamp; - + #[cgp_new_provider] impl CurrentTimeGetter for GetSystemTimestamp { fn current_time(_context: &Context) -> Result { let now = SystemTime::now() @@ -86,23 +79,20 @@ pub mod contexts { use super::impls::*; use super::traits::*; + #[cgp_context] pub struct MockApp { pub auth_tokens_store: BTreeMap, } - pub struct MockAppComponents; - - impl HasComponents for MockApp { - type Components = MockAppComponents; - } - - delegate_components! { + delegate_and_check_components! { + CanUseMockApp for MockApp; MockAppComponents { CurrentTimeGetterComponent: GetSystemTimestamp, AuthTokenValidatorComponent: ValidateTokenIsNotExpired, } } + #[cgp_provider] impl AuthTokenExpiryFetcher for MockAppComponents { fn fetch_auth_token_expiry( context: &MockApp, @@ -115,10 +105,6 @@ pub mod contexts { .ok_or_else(|| anyhow!("invalid auth token")) } } - - pub trait CanUseMockApp: CanValidateAuthToken {} - - impl CanUseMockApp for MockApp {} } # # } @@ -155,18 +141,12 @@ A more flexible approach is to define an _abstract_ `Time` type that allows us t # use cgp::prelude::*; -#[cgp_component { - name: TimeTypeComponent, - provider: ProvideTimeType, -}] +#[cgp_component(TimeTypeProviderComponent)] pub trait HasTimeType { type Time: Eq + Ord; } -#[cgp_component { - name: AuthTokenTypeComponent, - provider: ProvideAuthTokenType, -}] +#[cgp_component(AuthTokenTypeProviderComponent)] pub trait HasAuthTokenType { type AuthToken; } @@ -174,7 +154,7 @@ pub trait HasAuthTokenType { Here, we define the `HasTimeType` trait with an associated type `Time`, which is constrained to types that implement `Eq` and `Ord` so that they can be compared. Similarly, the `HasAuthTokenType` trait defines an associated type `AuthToken`, without any additional constraints. -Similar to regular trait methods, CGP allows us to auto-derive blanket implementations that delegate the associated types to providers using `HasComponents` and `DelegateComponent`. Therefore, we can use `#[cgp_component]` on traits containing associated types as well. +Similar to regular trait methods, CGP allows us to auto-derive blanket implementations that delegate the associated types to providers using `HasCgpProvider` and `DelegateComponent`. Therefore, we can use `#[cgp_component]` on traits containing associated types as well. With these type traits in place, we can now update our authentication components to leverage abstract types within the trait methods: @@ -187,39 +167,27 @@ With these type traits in place, we can now update our authentication components # use anyhow::Error; # use cgp::prelude::*; # -# #[cgp_component { -# name: TimeTypeComponent, -# provider: ProvideTimeType, -# }] +# #[cgp_component(TimeTypeProviderComponent)] # pub trait HasTimeType { # type Time: Eq + Ord; # } # -# #[cgp_component { -# name: AuthTokenTypeComponent, -# provider: ProvideAuthTokenType, -# }] +# #[cgp_component(AuthTokenTypeProvider)] # pub trait HasAuthTokenType { # type AuthToken; # } # -#[cgp_component { - provider: AuthTokenValidator, -}] +#[cgp_component(AuthTokenValidator)] pub trait CanValidateAuthToken: HasAuthTokenType { fn validate_auth_token(&self, auth_token: &Self::AuthToken) -> Result<(), Error>; } -#[cgp_component { - provider: AuthTokenExpiryFetcher, -}] +#[cgp_component(AuthTokenExpiryFetcher)] pub trait CanFetchAuthTokenExpiry: HasAuthTokenType + HasTimeType { fn fetch_auth_token_expiry(&self, auth_token: &Self::AuthToken) -> Result; } -#[cgp_component { - provider: CurrentTimeGetter, -}] +#[cgp_component(CurrentTimeGetter)] pub trait HasCurrentTime: HasTimeType { fn current_time(&self) -> Result; } @@ -236,45 +204,32 @@ With the abstract types defined, we can now update `ValidateTokenIsNotExpired` t # use anyhow::{anyhow, Error}; # use cgp::prelude::*; # -# #[cgp_component { -# name: TimeTypeComponent, -# provider: ProvideTimeType, -# }] +# #[cgp_component(TimeTypeProviderComponent)] # pub trait HasTimeType { # type Time: Eq + Ord; # } # -# #[cgp_component { -# name: AuthTokenTypeComponent, -# provider: ProvideAuthTokenType, -# }] +# #[cgp_component(AuthTokenTypeProviderComponent)] # pub trait HasAuthTokenType { # type AuthToken; # } # -# #[cgp_component { -# provider: AuthTokenValidator, -# }] +# #[cgp_component(AuthTokenValidator)] # pub trait CanValidateAuthToken: HasAuthTokenType { # fn validate_auth_token(&self, auth_token: &Self::AuthToken) -> Result<(), Error>; # } # -# #[cgp_component { -# provider: AuthTokenExpiryFetcher, -# }] +# #[cgp_component(AuthTokenExpiryFetcher)] # pub trait CanFetchAuthTokenExpiry: HasAuthTokenType + HasTimeType { # fn fetch_auth_token_expiry(&self, auth_token: &Self::AuthToken) -> Result; # } # -# #[cgp_component { -# provider: CurrentTimeGetter, -# }] +# #[cgp_component(CurrentTimeGetter)] # pub trait HasCurrentTime: HasTimeType { # fn current_time(&self) -> Result; # } # -pub struct ValidateTokenIsNotExpired; - +#[cgp_new_provider] impl AuthTokenValidator for ValidateTokenIsNotExpired where Context: HasCurrentTime + CanFetchAuthTokenExpiry, @@ -298,75 +253,65 @@ where This example shows how CGP enables us to define context-generic providers that are not just generic over the context itself, but also over its associated types. Unlike traditional generic programming, where all generic parameters are specified positionally, CGP allows us to parameterize abstract types using _names_ via associated types. -## Defining Abstract Type Traits with `cgp_type!` +## Defining Abstract Type Traits with `#[cgp_type]` -The type traits `HasTimeType` and `HasAuthTokenType` share a similar structure, and as you define more abstract types, this boilerplate can become tedious. To streamline the process, the `cgp` crate provides the `cgp_type!` macro, which simplifies type trait definitions. +The type traits `HasTimeType` and `HasAuthTokenType` share a similar structure, and as you define more abstract types, this boilerplate can become tedious. To streamline the process, the `cgp` crate provides the `#[cgp_type]` macro, which simplifies type trait definitions. -Here's how you can define the same types with `cgp_type!`: +Here's how you can define the same types with `#[cgp_type]`: ```rust # extern crate cgp; # use cgp::prelude::*; -cgp_type!( Time: Eq + Ord ); -cgp_type!( AuthToken ); -``` - -The `cgp_type!` macro accepts the name of an abstract type, `$name`, along with any applicable constraints for that type. It then automatically generates the same implementation as the `cgp_component` macro: a consumer trait named `Has{$name}Type`, a provider trait named `Provide{$name}Type`, and a component name type named `${name}TypeComponent`. Each of the generated traits includes an associated type defined as `type $name: $constraints;`. -In addition, `cgp_type!` also derives some other implementations, which we'll explore in later chapters. +#[cgp_type { + provider: TimeTypeProvider, +}] +pub trait HasTimeType { + type Time: Eq + Ord; +} -## Trait Minimalism +#[cgp_type { + provider: AuthTokenTypeProvider, +}] +pub trait HasAuthTokenType { + type AuthToken; +} +``` -At first glance, it might seem overly verbose to define multiple type traits and require each to be explicitly included as a supertrait of a method interface. For instance, you might be tempted to consolidate the methods and types into a single trait, like this: +The `#[cgp_type]` macro works with a CGP trait that contains a single non-generic associated type. It is an extension over `#[cgp_component]`, and generate additional constructs that make it easy to work with abstract types in CGP. When no argument is given, `#[cgp_type]` would default to generate a provider with name `{Type}TypeProvider`, and a component name `{Type}TypeProviderComponent`, where `{Type}` is the name of the associated type in the trait. So the above example can be shortened to: ```rust # extern crate cgp; -# extern crate anyhow; # -# use cgp::prelude::*; -# use anyhow::Error; -# -#[cgp_component { - provider: AppImpl, -}] -pub trait AppTrait { +use cgp::prelude::*; + +#[cgp_type] +pub trait HasTimeType { type Time: Eq + Ord; +} +#[cgp_type] +pub trait HasAuthTokenType { type AuthToken; - - fn validate_auth_token(&self, auth_token: &Self::AuthToken) -> Result<(), Error>; - - fn fetch_auth_token_expiry(&self, auth_token: &Self::AuthToken) -> Result; - - fn current_time(&self) -> Result; } ``` -While this approach might seem simpler, it introduces unnecessary _coupling_ between -potentially unrelated types and methods. For example, an application implementing -token validation might delegate this functionality to an external microservice. -In such a case, it is redundant to require the application to specify a Time type that -it doesn’t actually use. - -In practice, we find the practical benefits of defining many _minimal_ traits often -outweight any theoretical advantages of combining multiple items into one trait. -As we will demonstrate in later chapters, having traits that contain only one type -or method would also enable more advanced CGP patterns to be applied, including -the use of `cgp_type!` that we have just covered. - -We encourage readers to embrace minimal traits without concern for theoretical overhead. However, during the early phases of a project, you might prefer to consolidate items to reduce cognitive overload while learning or prototyping. As the project matures, you can always refactor and decompose larger traits into smaller, more focused ones, following the techniques outlined in this book. +We will explore in a moment how using `#[cgp_type]` with a single associated type bring more convenience, as compared to alternative approaches. ## Impl-Side Associated Type Constraints -The minimalism philosophy of CGP extends to the constraints placed on associated types within type traits. Consider the earlier definition of `HasTimeType`: +The dependency-injection capabilities of CGP opens up new choices of how to design the abstract type interfaces. Consider the earlier definition of `HasTimeType`: ```rust # extern crate cgp; # # use cgp::prelude::*; # -cgp_type!( Time: Eq + Ord ); +#[cgp_type] +pub trait HasTimeType { + type Time: Eq + Ord; +} ``` Here, the associated `Time` type is constrained by `Eq + Ord`. This means that all concrete implementations of `Time` must satisfy these constraints, regardless of whether they are actually required by the providers. In fact, if we revisit our previous examples, we notice that the `Eq` constraint isn’t used anywhere. @@ -382,33 +327,32 @@ Fortunately, CGP allows us to apply the same principle of impl-side dependencies # use anyhow::{anyhow, Error}; # use cgp::prelude::*; # -cgp_type!( Time ); - -# cgp_type!( AuthToken ); +#[cgp_type] +pub trait HasTimeType { + type Time: Eq + Ord; +} # -# #[cgp_component { -# provider: AuthTokenValidator, -# }] +# #[cgp_type] +# pub trait HasAuthTokenType { +# type AuthToken; +# } +# +# #[cgp_component(AuthTokenValidator)] # pub trait CanValidateAuthToken: HasAuthTokenType { # fn validate_auth_token(&self, auth_token: &Self::AuthToken) -> Result<(), Error>; # } # -# #[cgp_component { -# provider: AuthTokenExpiryFetcher, -# }] +# #[cgp_component(AuthTokenExpiryFetcher)] # pub trait CanFetchAuthTokenExpiry: HasAuthTokenType + HasTimeType { # fn fetch_auth_token_expiry(&self, auth_token: &Self::AuthToken) -> Result; # } # -# #[cgp_component { -# provider: CurrentTimeGetter, -# }] +# #[cgp_component(CurrentTimeGetter)] # pub trait HasCurrentTime: HasTimeType { # fn current_time(&self) -> Result; # } -# -pub struct ValidateTokenIsNotExpired; +#[cgp_new_provider] impl AuthTokenValidator for ValidateTokenIsNotExpired where Context: HasCurrentTime + CanFetchAuthTokenExpiry, @@ -437,7 +381,7 @@ By applying constraints on the implementation side, we can conditionally require In some cases, it can still be convenient to include constraints (e.g., `Debug`) directly on an associated type, especially if those constraints are nearly universal across providers. Additionally, current Rust error reporting often produces clearer error messages when constraints are defined at the associated type level, as opposed to being deferred to the implementation. -As a guideline, we recommend that readers begin by defining type traits without placing constraints on associated types, relying instead on implementation-side constraints wherever possible. However, readers may choose to apply global constraints to associated types when appropriate, particularly for simple and widely applicable traits like `Debug` and `Eq`. +Ultimately, CGP does not prevent its users from preferring one design approach over another. The minimalistic abstract type design is one that you will likely see often in CGP code, particularly in this book. However, do not hesitate to include addititional trait bounds based on your requirements and preferences! ## Type Providers @@ -452,10 +396,7 @@ With type abstraction in place, we can define context-generic providers for the # use cgp::prelude::*; # use anyhow::Error; # -# #[cgp_component { -# name: TimeTypeComponent, -# provider: ProvideTimeType, -# }] +# #[cgp_type] # pub trait HasTimeType { # type Time: Eq + Ord; # } @@ -469,10 +410,12 @@ With type abstraction in place, we can define context-generic providers for the # pub struct UseInstant; -impl ProvideTimeType for UseInstant { +#[cgp_provider] +impl TimeTypeProvider for UseInstant { type Time = Instant; } +#[cgp_provider] impl CurrentTimeGetter for UseInstant where Context: HasTimeType