refactor: implement builder pattern for OmnectDeviceServiceClient with lifecycle management#65
Conversation
161520c to
ad6b10b
Compare
…h lifecycle management This refactoring improves the architecture of OmnectDeviceServiceClient by: - Implementing a type-safe builder pattern as the only way to create the client - Making certificate setup optional and injectable via closure - Making publish endpoint registration optional - Extracting client creation into dedicated build_service_client() function - Bumping version to 1.0.6 Key changes: - OmnectDeviceServiceClient fields are now private, enforcing builder usage - Builder supports optional certificate setup via with_certificate_setup() - Builder supports optional publish endpoint via with_publish_endpoint() - Certificate payload (CreateCertPayload) is now public and reusable - Removed intermediate WorkloadCertPayload struct for simplicity - Shutdown is now a DeviceServiceClient trait method called explicitly from main - Improved Dockerfile to copy entire src directory structure This design provides better encapsulation, clearer lifecycle management, and looser coupling between modules through dependency injection. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
ad6b10b to
58fa57d
Compare
src/omnect_device_service_client.rs
Outdated
| pub struct OmnectDeviceServiceClientBuilder<F, Fut> | ||
| where | ||
| F: FnOnce(CreateCertPayload) -> Fut, | ||
| Fut: std::future::Future<Output = Result<()>>, | ||
| { | ||
| publish_endpoint: Option<PublishEndpoint>, | ||
| certificate_setup: Option<F>, | ||
| _phantom: std::marker::PhantomData<Fut>, | ||
| } | ||
|
|
There was a problem hiding this comment.
I wonder whether we really need to make OmnectDeviceServiceClientBuilder generic over the Fut type. If not, we can drop the _phantom marker and simplify it a little bit.
If I understand the code correctly, Fut is only necessary as a bound for F for the result type? wouldn't it simplify things if we just had
pub struct OmnectDeviceServiceClientBuilder<F>
where
F: FnOnce(CreateCertPayload) -> std::future::Future<Output = Result<()>>,
{
...instead?
Alternatively, we should provide a default type for the Fut in the generic type declaration to simplify things at the call site.
There was a problem hiding this comment.
Good catch! I've simplified the builder by removing the Fut generic parameter and PhantomData. Changes:
- Builder is no longer generic over F and Fut
- Uses boxed trait objects for the certificate setup function
- Split the type alias into two for better readability:
- type CertSetupFuture = Pin<Box<dyn Future<Output = Result<()>>>>;
- type CertSetupFn = Box<dyn FnOnce(CreateCertPayload) -> CertSetupFuture>;
The API remains identical for callers, and the small runtime cost (heap allocation + dynamic dispatch) is negligible since it only happens once during initialization. The improved simplicity is worth the trade-off.
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Summary
This PR refactors
OmnectDeviceServiceClientto implement a type-safe builder pattern with lifecycle management, improving encapsulation and maintainability.Key Changes
OmnectDeviceServiceClient, with private struct fieldswith_certificate_setup()with_publish_endpoint()build_service_client()functionCreateCertPayloadpublic and removed intermediateWorkloadCertPayloadsrcdirectory structureBenefits