public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Lukas Wagner <l.wagner@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [PATCH proxmox 01/20] router: introduce shared state
Date: Mon, 17 Aug 2026 14:57:08 +0200	[thread overview]
Message-ID: <20260817125727.454039-2-l.wagner@proxmox.com> (raw)
In-Reply-To: <20260817125727.454039-1-l.wagner@proxmox.com>

API handlers often need access to long-lived application data such as
configuration, caches or client handles. So far the only way to get
there is a global static, which hides the actual dependencies of a
handler and makes testing awkward.

Add a type-keyed registry that a server fills once during startup,
together with an accessor on RpcEnvironment so that handlers can reach
it. The State<T> newtype wraps values that come from the registry,
which allows telling them apart from regular API parameters.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 proxmox-router/src/cli/environment.rs | 12 ++++++-
 proxmox-router/src/lib.rs             |  2 ++
 proxmox-router/src/rpc_environment.rs |  7 ++++
 proxmox-router/src/shared_state.rs    | 46 +++++++++++++++++++++++++++
 4 files changed, 66 insertions(+), 1 deletion(-)
 create mode 100644 proxmox-router/src/shared_state.rs

diff --git a/proxmox-router/src/cli/environment.rs b/proxmox-router/src/cli/environment.rs
index c85105a7..c9aefb75 100644
--- a/proxmox-router/src/cli/environment.rs
+++ b/proxmox-router/src/cli/environment.rs
@@ -5,7 +5,7 @@ use serde_json::Value;
 
 use proxmox_schema::ApiType;
 
-use crate::{RpcEnvironment, RpcEnvironmentType};
+use crate::{RpcEnvironment, RpcEnvironmentType, SharedStateRegistry};
 
 /// [`RpcEnvironment`] implementation for command line tools.
 ///
@@ -15,6 +15,7 @@ use crate::{RpcEnvironment, RpcEnvironmentType};
 pub struct CliEnvironment {
     result_attributes: Value,
     auth_id: Option<String>,
+    shared_state_registry: Option<SharedStateRegistry>,
     pub(crate) global_options: HashMap<TypeId, Box<dyn Any + Send + Sync + 'static>>,
 }
 
@@ -23,6 +24,11 @@ impl CliEnvironment {
         Default::default()
     }
 
+    /// Set the shared state registry for this environment.
+    pub fn set_shared_state_registry(&mut self, registry: SharedStateRegistry) {
+        self.shared_state_registry = Some(registry);
+    }
+
     /// Borrow a global option by type.
     ///
     /// Returns `None` if the option type was not registered or no value was provided on the
@@ -98,4 +104,8 @@ impl RpcEnvironment for CliEnvironment {
     fn get_auth_id(&self) -> Option<String> {
         self.auth_id.clone()
     }
+
+    fn shared_state(&self) -> Option<&SharedStateRegistry> {
+        self.shared_state_registry.as_ref()
+    }
 }
diff --git a/proxmox-router/src/lib.rs b/proxmox-router/src/lib.rs
index da2f018f..df225d25 100644
--- a/proxmox-router/src/lib.rs
+++ b/proxmox-router/src/lib.rs
@@ -16,6 +16,7 @@ mod permission;
 mod router;
 mod rpc_environment;
 mod serializable_return;
+mod shared_state;
 
 #[doc(inline)]
 #[cfg(feature = "server")]
@@ -25,6 +26,7 @@ pub use permission::*;
 pub use router::*;
 pub use rpc_environment::{RpcEnvironment, RpcEnvironmentType};
 pub use serializable_return::SerializableReturn;
+pub use shared_state::{SharedStateRegistry, State};
 
 // make list_subdirs_api_method! work without an explicit proxmox-schema dependency:
 #[doc(hidden)]
diff --git a/proxmox-router/src/rpc_environment.rs b/proxmox-router/src/rpc_environment.rs
index 8ce2d99d..e065504a 100644
--- a/proxmox-router/src/rpc_environment.rs
+++ b/proxmox-router/src/rpc_environment.rs
@@ -2,6 +2,8 @@ use std::any::Any;
 
 use serde_json::Value;
 
+use crate::SharedStateRegistry;
+
 /// Helper to get around `RpcEnvironment: Sized`
 pub trait AsAny {
     fn as_any(&self) -> &(dyn Any + Send);
@@ -45,6 +47,11 @@ pub trait RpcEnvironment: Any + AsAny + Send {
     fn get_client_ip(&self) -> Option<std::net::SocketAddr> {
         None // dummy no-op implementation, as most environments don't need this
     }
+
+    /// Return a reference to the shared state registry.
+    fn shared_state(&self) -> Option<&SharedStateRegistry> {
+        None
+    }
 }
 
 /// Environment Type
diff --git a/proxmox-router/src/shared_state.rs b/proxmox-router/src/shared_state.rs
new file mode 100644
index 00000000..14a539af
--- /dev/null
+++ b/proxmox-router/src/shared_state.rs
@@ -0,0 +1,46 @@
+//! Type-keyed state that API handlers can request as a parameter.
+
+use std::any::{Any, TypeId};
+use std::collections::HashMap;
+
+use anyhow::{Error, bail};
+
+/// Registry of state values, keyed by their type.
+///
+/// It holds at most one value per type. Values are registered with
+/// [`register`](SharedStateRegistry::register) before the registry is handed over to the API
+/// environment, from where handlers can access them through
+/// [`RpcEnvironment::shared_state`](crate::RpcEnvironment::shared_state). This allows passing
+/// application context to handlers without resorting to globals.
+#[derive(Default)]
+pub struct SharedStateRegistry {
+    map: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
+}
+
+impl SharedStateRegistry {
+    /// Get a clone of the registered value of type `T`, if there is one.
+    pub fn lookup<T: 'static + Send + Sync + Clone>(&self) -> Option<T> {
+        self.map
+            .get(&TypeId::of::<T>())
+            .and_then(|s| s.downcast_ref())
+            .cloned()
+    }
+
+    /// Register a value so that handlers can request it as a `State<T>` parameter.
+    ///
+    /// Since the type is the key, wrap values in a newtype if their type alone is not specific
+    /// enough to identify them.
+    ///
+    /// Fails if a value of type `T` was already registered.
+    pub fn register<T: 'static + Send + Sync + Clone>(&mut self, data: T) -> Result<(), Error> {
+        let type_id = TypeId::of::<T>();
+
+        if self.map.contains_key(&type_id) {
+            bail!("type already registered");
+        }
+
+        self.map.insert(type_id, Box::new(data));
+
+        Ok(())
+    }
+}
-- 
2.47.3





  reply	other threads:[~2026-08-17 12:57 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-17 12:57 [PATCH datacenter-manager/proxmox 00/20] inject application context via API macro for easier integration testing Lukas Wagner
2026-08-17 12:57 ` Lukas Wagner [this message]
2026-08-17 13:26   ` [PATCH proxmox 01/20] router: introduce shared state Lukas Wagner
2026-08-20 11:23   ` Lukas Wagner
2026-08-21 13:59   ` Robert Obkircher
2026-08-17 12:57 ` [PATCH proxmox 02/20] rest-server: allow to inject " Lukas Wagner
2026-08-17 12:57 ` [PATCH proxmox 03/20] api-macro: support shared state extraction type Lukas Wagner
2026-08-21 13:59   ` Robert Obkircher
2026-08-17 12:57 ` [PATCH datacenter-manager 04/20] context: promote context to a dir-style module Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 05/20] pdm-config: remotes: rename trait methods to read/write/lock Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 06/20] pdm-config: subscriptions: " Lukas Wagner
2026-08-21 14:00   ` Robert Obkircher
2026-08-17 12:57 ` [PATCH datacenter-manager 07/20] remote iterator: pass remote config reader explicitly Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 08/20] context: introduce a ContextFactory to build application context Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 09/20] context: establish PdmApplication object Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 10/20] context: register PdmApplication in router Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 11/20] parallel fetcher: pass arguments to closure in a single type Lukas Wagner
2026-08-21 14:00   ` Robert Obkircher
2026-08-17 12:57 ` [PATCH datacenter-manager 12/20] parallel fetcher: support a custom client factory Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 13/20] api: sdn: use PdmApplication handle for accessing remotes Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 14/20] tests: add helpers for building API-handler-level integration tests Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 15/20] tests: add example tests for SDN API routes Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 16/20] api-cache: add wrapper type Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 17/20] context: provide api-cache on the app object Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 18/20] api: subscriptions: use PdmApplication instead of globals Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 19/20] pdm-config: subscriptions: drop unused accessor functions Lukas Wagner
2026-08-17 12:57 ` [PATCH datacenter-manager 20/20] tests: add example tests for remote subscription management Lukas Wagner
2026-08-20 14:54 ` superseded: [PATCH datacenter-manager/proxmox 00/20] inject application context via API macro for easier integration testing Lukas Wagner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260817125727.454039-2-l.wagner@proxmox.com \
    --to=l.wagner@proxmox.com \
    --cc=pdm-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal