1use std::sync::Arc;
4
5use crate::public::auth::Auth;
6use crate::public::generated::app_client::{
7 AgentClient, AppClient, AuthorizationClient, ExternalCredentialsClient, IdentityClient,
8 IndexedDBClient, WorkflowClient,
9};
10use crate::public::generated::rpc_support::GestaltError;
11use crate::public::grpc_transport::{GrpcTransport, dial_public_grpc};
12use crate::public::rest_transport::RestTransport;
13use crate::rpc_support::gestalt_error_code;
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub enum Transport {
18 Rest,
20 Grpc,
22}
23
24pub fn rest() -> Transport {
26 Transport::Rest
27}
28
29pub fn grpc() -> Transport {
31 Transport::Grpc
32}
33
34pub enum GestaltClient {
36 Rest(AppClient<RestTransport>),
38 Grpc(Box<AppClient<GrpcTransport>>),
40}
41
42pub struct RestGestaltClient {
44 pub app: AppClient<RestTransport>,
46 pub agent: AgentClient<RestTransport>,
48 pub workflow: WorkflowClient<RestTransport>,
50 pub identity: IdentityClient<RestTransport>,
52 pub authorization: AuthorizationClient<RestTransport>,
54}
55
56impl RestGestaltClient {
57 pub fn close(self) {}
59}
60
61pub struct GrpcGestaltClient {
63 pub app: AppClient<GrpcTransport>,
65 pub agent: AgentClient<GrpcTransport>,
67 pub workflow: WorkflowClient<GrpcTransport>,
69 pub identity: IdentityClient<GrpcTransport>,
71 pub authorization: AuthorizationClient<GrpcTransport>,
73 pub indexed_db: IndexedDBClient<GrpcTransport>,
75 pub external_credentials: ExternalCredentialsClient<GrpcTransport>,
77}
78
79impl GrpcGestaltClient {
80 pub fn close(self) {}
82}
83
84pub async fn create_gestalt_client<A: Auth + 'static>(
86 address: impl Into<String>,
87 auth: A,
88 transport: Transport,
89) -> Result<GestaltClient, GestaltError> {
90 let address = normalize_address(address.into())?;
91 let auth: Arc<dyn Auth> = Arc::new(auth);
92 match transport {
93 Transport::Rest => Ok(GestaltClient::Rest(AppClient::new(RestTransport::new(
94 address,
95 Arc::clone(&auth),
96 )))),
97 Transport::Grpc => {
98 let channel = dial_public_grpc(&address)?.connect_lazy();
99 Ok(GestaltClient::Grpc(Box::new(AppClient::new(
100 GrpcTransport::new(channel, auth),
101 ))))
102 }
103 }
104}
105
106pub async fn create_rest_gestalt_client<A: Auth + 'static>(
108 address: impl Into<String>,
109 auth: A,
110) -> Result<RestGestaltClient, GestaltError> {
111 let address = normalize_address(address.into())?;
112 let auth: Arc<dyn Auth> = Arc::new(auth);
113 Ok(bind_rest(RestTransport::new(address, auth)))
114}
115
116pub async fn create_grpc_gestalt_client<A: Auth + 'static>(
118 address: impl Into<String>,
119 auth: A,
120) -> Result<GrpcGestaltClient, GestaltError> {
121 let address = normalize_address(address.into())?;
122 let auth: Arc<dyn Auth> = Arc::new(auth);
123 let channel = dial_public_grpc(&address)?.connect_lazy();
124 Ok(bind_grpc(GrpcTransport::new(channel, auth)))
125}
126
127fn bind_rest(transport: RestTransport) -> RestGestaltClient {
128 RestGestaltClient {
129 app: AppClient::new(transport.clone()),
130 agent: AgentClient::new(transport.clone()),
131 workflow: WorkflowClient::new(transport.clone()),
132 identity: IdentityClient::new(transport.clone()),
133 authorization: AuthorizationClient::new(transport.clone()),
134 }
135}
136
137fn bind_grpc(transport: GrpcTransport) -> GrpcGestaltClient {
138 GrpcGestaltClient {
139 app: AppClient::new(transport.clone()),
140 agent: AgentClient::new(transport.clone()),
141 workflow: WorkflowClient::new(transport.clone()),
142 identity: IdentityClient::new(transport.clone()),
143 authorization: AuthorizationClient::new(transport.clone()),
144 indexed_db: IndexedDBClient::new(transport.clone()),
145 external_credentials: ExternalCredentialsClient::new(transport),
146 }
147}
148
149fn normalize_address(address: String) -> Result<String, GestaltError> {
150 let address = address.trim();
151 if address.is_empty() {
152 return Err(GestaltError::new(
153 gestalt_error_code::INVALID_ARGUMENT,
154 "address is required for external clients (use gestalt_from_context for bound provider access)",
155 ));
156 }
157 Ok(address.trim_end_matches('/').to_string())
158}