this/server/host.rs
1//! Server host for transport-agnostic API exposure
2//!
3//! This module provides a `ServerHost` structure that contains all framework state
4//! needed to expose the API via any protocol (REST, GraphQL, gRPC, etc.)
5//!
6//! The host is completely agnostic to the transport protocol and serves as the
7//! single source of truth for the application state.
8
9use crate::config::LinksConfig;
10use crate::core::events::EventBus;
11use crate::core::{EntityCreator, EntityFetcher, service::LinkService};
12use crate::links::registry::LinkRouteRegistry;
13use crate::server::entity_registry::EntityRegistry;
14use anyhow::Result;
15use std::collections::HashMap;
16use std::sync::Arc;
17
18/// Host context containing all framework state
19///
20/// This structure is transport-agnostic and contains all the information
21/// needed to expose the API via any protocol (REST, GraphQL, gRPC, etc.)
22///
23/// # Example
24///
25/// ```rust,ignore
26/// let host = ServerHost::from_builder_components(
27/// link_service,
28/// config,
29/// entity_registry,
30/// fetchers,
31/// creators,
32/// )?;
33///
34/// // Use host with any exposure
35/// let host_arc = Arc::new(host);
36/// let rest_app = RestExposure::build_router(host_arc.clone())?;
37/// let graphql_app = GraphQLExposure::build_router(host_arc)?;
38/// ```
39pub struct ServerHost {
40 /// Merged configuration from all modules
41 pub config: Arc<LinksConfig>,
42
43 /// Link service for relationship management
44 pub link_service: Arc<dyn LinkService>,
45
46 /// Link route registry for semantic URL resolution
47 pub registry: Arc<LinkRouteRegistry>,
48
49 /// Entity registry for CRUD routes
50 pub entity_registry: EntityRegistry,
51
52 /// Entity fetchers map (for link enrichment)
53 pub entity_fetchers: Arc<HashMap<String, Arc<dyn EntityFetcher>>>,
54
55 /// Entity creators map (for automatic entity + link creation)
56 pub entity_creators: Arc<HashMap<String, Arc<dyn EntityCreator>>>,
57
58 /// Optional event bus for real-time notifications (WebSocket, SSE)
59 ///
60 /// When present, REST/GraphQL handlers will publish events for mutations.
61 /// WebSocket and other real-time exposures subscribe to this bus.
62 pub event_bus: Option<Arc<EventBus>>,
63}
64
65impl ServerHost {
66 /// Build the host from builder components
67 ///
68 /// This method takes all the components that have been registered with
69 /// the builder and constructs the host structure.
70 ///
71 /// # Arguments
72 ///
73 /// * `link_service` - The link service for relationship management
74 /// * `config` - Merged configuration from all modules
75 /// * `entity_registry` - Registry of all entity descriptors
76 /// * `fetchers` - Map of entity type to fetcher implementation
77 /// * `creators` - Map of entity type to creator implementation
78 ///
79 /// # Returns
80 ///
81 /// Returns a `ServerHost` ready to be used with any exposure (REST, GraphQL, gRPC, etc.)
82 pub fn from_builder_components(
83 link_service: Arc<dyn LinkService>,
84 config: LinksConfig,
85 entity_registry: EntityRegistry,
86 fetchers: HashMap<String, Arc<dyn EntityFetcher>>,
87 creators: HashMap<String, Arc<dyn EntityCreator>>,
88 ) -> Result<Self> {
89 let config = Arc::new(config);
90 let registry = Arc::new(LinkRouteRegistry::new(config.clone()));
91
92 Ok(Self {
93 config,
94 link_service,
95 registry,
96 entity_registry,
97 entity_fetchers: Arc::new(fetchers),
98 entity_creators: Arc::new(creators),
99 event_bus: None,
100 })
101 }
102
103 /// Get entity types registered in the host
104 pub fn entity_types(&self) -> Vec<&str> {
105 self.entity_registry.entity_types()
106 }
107
108 /// Check if host is properly initialized
109 pub fn is_ready(&self) -> bool {
110 !self.entity_fetchers.is_empty()
111 }
112
113 /// Set the event bus for real-time notifications
114 pub fn with_event_bus(mut self, event_bus: EventBus) -> Self {
115 self.event_bus = Some(Arc::new(event_bus));
116 self
117 }
118
119 /// Get a reference to the event bus (if configured)
120 pub fn event_bus(&self) -> Option<&Arc<EventBus>> {
121 self.event_bus.as_ref()
122 }
123}