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
next prev parent 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