use crate::{commands::*, responses::*, utils::CommandSyntax, *};
use std::future::Future;
use std::sync::Arc;
pub trait ExtractResponse<'de, T>: Deserialize<'de> {
fn extract_response(self) -> Result<T, BadResponseError>;
}
pub trait ClientApiError: From<BadResponseError> + std::error::Error {
fn bad_response(&self) -> Option<&BadResponseError>;
fn bad_response_mut(&mut self) -> Option<&mut BadResponseError>;
}
pub trait ClientApi: Sync {
type ResponseShape<'de, T>: ExtractResponse<'de, T>
where
T: 'de + Deserialize<'de>;
type Error: ClientApiError;
fn send_raw(&self, command: String)
-> impl Future<Output = Result<String, Self::Error>> + Send;
fn send<C, R>(&self, cmd: C) -> impl Future<Output = Result<R, Self::Error>> + Send
where
C: Send + CommandSyntax,
R: for<'de> Deserialize<'de>,
{
async move {
let raw = self.send_raw(cmd.to_command_string()).await?;
let response_shape: Self::ResponseShape<'_, R> =
serde_json::from_str(&raw).map_err(BadResponseError::InvalidJson)?;
let response = response_shape.extract_response()?;
Ok(response)
}
}
fn api_create_my_address(
&self,
user_id: i64,
) -> impl Future<Output = Result<Arc<UserContactLinkCreatedResponse>, Self::Error>> + Send {
async move {
let command = ApiCreateMyAddress { user_id };
let response: ApiCreateMyAddressResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_delete_my_address(
&self,
user_id: i64,
) -> impl Future<Output = Result<Arc<UserContactLinkDeletedResponse>, Self::Error>> + Send {
async move {
let command = ApiDeleteMyAddress { user_id };
let response: ApiDeleteMyAddressResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_show_my_address(
&self,
user_id: i64,
) -> impl Future<Output = Result<Arc<UserContactLinkResponse>, Self::Error>> + Send {
async move {
let command = ApiShowMyAddress { user_id };
let response: ApiShowMyAddressResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_set_profile_address(
&self,
command: ApiSetProfileAddress,
) -> impl Future<Output = Result<Arc<UserProfileUpdatedResponse>, Self::Error>> + Send {
async move {
let response: ApiSetProfileAddressResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_set_address_settings(
&self,
user_id: i64,
settings: AddressSettings,
) -> impl Future<Output = Result<Arc<UserContactLinkUpdatedResponse>, Self::Error>> + Send {
async move {
let command = ApiSetAddressSettings { user_id, settings };
let response: ApiSetAddressSettingsResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_send_messages(
&self,
command: ApiSendMessages,
) -> impl Future<Output = Result<Arc<NewChatItemsResponse>, Self::Error>> + Send {
async move {
let response: ApiSendMessagesResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_update_chat_item(
&self,
command: ApiUpdateChatItem,
) -> impl Future<Output = Result<ApiUpdateChatItemResponse, Self::Error>> + Send {
async move {
let response: ApiUpdateChatItemResponse = self.send(command).await?;
Ok(response)
}
}
fn api_delete_chat_item(
&self,
chat_ref: ChatRef,
chat_item_ids: Vec<i64>,
delete_mode: CIDeleteMode,
) -> impl Future<Output = Result<Arc<ChatItemsDeletedResponse>, Self::Error>> + Send {
async move {
let command = ApiDeleteChatItem {
chat_ref,
chat_item_ids,
delete_mode,
};
let response: ApiDeleteChatItemResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_delete_member_chat_item(
&self,
group_id: i64,
chat_item_ids: Vec<i64>,
) -> impl Future<Output = Result<Arc<ChatItemsDeletedResponse>, Self::Error>> + Send {
async move {
let command = ApiDeleteMemberChatItem {
group_id,
chat_item_ids,
};
let response: ApiDeleteMemberChatItemResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_chat_item_reaction(
&self,
command: ApiChatItemReaction,
) -> impl Future<Output = Result<Arc<ChatItemReactionResponse>, Self::Error>> + Send {
async move {
let response: ApiChatItemReactionResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn receive_file(
&self,
command: ReceiveFile,
) -> impl Future<Output = Result<ReceiveFileResponse, Self::Error>> + Send {
async move {
let response: ReceiveFileResponse = self.send(command).await?;
Ok(response)
}
}
fn cancel_file(
&self,
file_id: i64,
) -> impl Future<Output = Result<CancelFileResponse, Self::Error>> + Send {
async move {
let command = CancelFile { file_id };
let response: CancelFileResponse = self.send(command).await?;
Ok(response)
}
}
fn api_add_member(
&self,
group_id: i64,
contact_id: i64,
member_role: GroupMemberRole,
) -> impl Future<Output = Result<Arc<SentGroupInvitationResponse>, Self::Error>> + Send {
async move {
let command = ApiAddMember {
group_id,
contact_id,
member_role,
};
let response: ApiAddMemberResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_join_group(
&self,
group_id: i64,
) -> impl Future<Output = Result<Arc<UserAcceptedGroupSentResponse>, Self::Error>> + Send {
async move {
let command = ApiJoinGroup { group_id };
let response: ApiJoinGroupResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_accept_member(
&self,
group_id: i64,
group_member_id: i64,
member_role: GroupMemberRole,
) -> impl Future<Output = Result<Arc<MemberAcceptedResponse>, Self::Error>> + Send {
async move {
let command = ApiAcceptMember {
group_id,
group_member_id,
member_role,
};
let response: ApiAcceptMemberResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_members_role(
&self,
group_id: i64,
group_member_ids: Vec<i64>,
member_role: GroupMemberRole,
) -> impl Future<Output = Result<Arc<MembersRoleUserResponse>, Self::Error>> + Send {
async move {
let command = ApiMembersRole {
group_id,
group_member_ids,
member_role,
};
let response: ApiMembersRoleResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_block_members_for_all(
&self,
command: ApiBlockMembersForAll,
) -> impl Future<Output = Result<Arc<MembersBlockedForAllUserResponse>, Self::Error>> + Send
{
async move {
let response: ApiBlockMembersForAllResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_remove_members(
&self,
command: ApiRemoveMembers,
) -> impl Future<Output = Result<Arc<UserDeletedMembersResponse>, Self::Error>> + Send {
async move {
let response: ApiRemoveMembersResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_leave_group(
&self,
group_id: i64,
) -> impl Future<Output = Result<Arc<LeftMemberUserResponse>, Self::Error>> + Send {
async move {
let command = ApiLeaveGroup { group_id };
let response: ApiLeaveGroupResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_list_members(
&self,
group_id: i64,
) -> impl Future<Output = Result<Arc<GroupMembersResponse>, Self::Error>> + Send {
async move {
let command = ApiListMembers { group_id };
let response: ApiListMembersResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_new_group(
&self,
command: ApiNewGroup,
) -> impl Future<Output = Result<Arc<GroupCreatedResponse>, Self::Error>> + Send {
async move {
let response: ApiNewGroupResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_new_public_group(
&self,
command: ApiNewPublicGroup,
) -> impl Future<Output = Result<ApiNewPublicGroupResponse, Self::Error>> + Send {
async move {
let response: ApiNewPublicGroupResponse = self.send(command).await?;
Ok(response)
}
}
fn api_get_group_relays(
&self,
group_id: i64,
) -> impl Future<Output = Result<Arc<GroupRelaysResponse>, Self::Error>> + Send {
async move {
let command = ApiGetGroupRelays { group_id };
let response: ApiGetGroupRelaysResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_add_group_relays(
&self,
group_id: i64,
relay_ids: Vec<i64>,
) -> impl Future<Output = Result<ApiAddGroupRelaysResponse, Self::Error>> + Send {
async move {
let command = ApiAddGroupRelays {
group_id,
relay_ids,
};
let response: ApiAddGroupRelaysResponse = self.send(command).await?;
Ok(response)
}
}
fn api_update_group_profile(
&self,
group_id: i64,
group_profile: GroupProfile,
) -> impl Future<Output = Result<Arc<GroupUpdatedResponse>, Self::Error>> + Send {
async move {
let command = ApiUpdateGroupProfile {
group_id,
group_profile,
};
let response: ApiUpdateGroupProfileResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_create_group_link(
&self,
group_id: i64,
member_role: GroupMemberRole,
) -> impl Future<Output = Result<Arc<GroupLinkCreatedResponse>, Self::Error>> + Send {
async move {
let command = ApiCreateGroupLink {
group_id,
member_role,
};
let response: ApiCreateGroupLinkResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_group_link_member_role(
&self,
group_id: i64,
member_role: GroupMemberRole,
) -> impl Future<Output = Result<Arc<GroupLinkResponse>, Self::Error>> + Send {
async move {
let command = ApiGroupLinkMemberRole {
group_id,
member_role,
};
let response: ApiGroupLinkMemberRoleResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_delete_group_link(
&self,
group_id: i64,
) -> impl Future<Output = Result<Arc<GroupLinkDeletedResponse>, Self::Error>> + Send {
async move {
let command = ApiDeleteGroupLink { group_id };
let response: ApiDeleteGroupLinkResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_get_group_link(
&self,
group_id: i64,
) -> impl Future<Output = Result<Arc<GroupLinkResponse>, Self::Error>> + Send {
async move {
let command = ApiGetGroupLink { group_id };
let response: ApiGetGroupLinkResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_add_contact(
&self,
command: ApiAddContact,
) -> impl Future<Output = Result<Arc<InvitationResponse>, Self::Error>> + Send {
async move {
let response: ApiAddContactResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_connect_plan(
&self,
command: ApiConnectPlan,
) -> impl Future<Output = Result<Arc<ConnectionPlanResponse>, Self::Error>> + Send {
async move {
let response: ApiConnectPlanResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_connect(
&self,
command: ApiConnect,
) -> impl Future<Output = Result<ApiConnectResponse, Self::Error>> + Send {
async move {
let response: ApiConnectResponse = self.send(command).await?;
Ok(response)
}
}
fn connect(
&self,
command: Connect,
) -> impl Future<Output = Result<ConnectResponse, Self::Error>> + Send {
async move {
let response: ConnectResponse = self.send(command).await?;
Ok(response)
}
}
fn api_accept_contact(
&self,
contact_req_id: i64,
) -> impl Future<Output = Result<Arc<AcceptingContactRequestResponse>, Self::Error>> + Send
{
async move {
let command = ApiAcceptContact { contact_req_id };
let response: ApiAcceptContactResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_reject_contact(
&self,
contact_req_id: i64,
) -> impl Future<Output = Result<Arc<ContactRequestRejectedResponse>, Self::Error>> + Send {
async move {
let command = ApiRejectContact { contact_req_id };
let response: ApiRejectContactResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_list_contacts(
&self,
user_id: i64,
) -> impl Future<Output = Result<Arc<ContactsListResponse>, Self::Error>> + Send {
async move {
let command = ApiListContacts { user_id };
let response: ApiListContactsResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_list_groups(
&self,
command: ApiListGroups,
) -> impl Future<Output = Result<Arc<GroupsListResponse>, Self::Error>> + Send {
async move {
let response: ApiListGroupsResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_get_chats(
&self,
command: ApiGetChats,
) -> impl Future<Output = Result<Arc<ApiChatsResponse>, Self::Error>> + Send {
async move {
let response: ApiGetChatsResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_delete_chat(
&self,
chat_ref: ChatRef,
chat_delete_mode: ChatDeleteMode,
) -> impl Future<Output = Result<ApiDeleteChatResponse, Self::Error>> + Send {
async move {
let command = ApiDeleteChat {
chat_ref,
chat_delete_mode,
};
let response: ApiDeleteChatResponse = self.send(command).await?;
Ok(response)
}
}
fn api_set_group_custom_data(
&self,
command: ApiSetGroupCustomData,
) -> impl Future<Output = Result<Arc<CmdOkResponse>, Self::Error>> + Send {
async move {
let response: ApiSetGroupCustomDataResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_set_contact_custom_data(
&self,
command: ApiSetContactCustomData,
) -> impl Future<Output = Result<Arc<CmdOkResponse>, Self::Error>> + Send {
async move {
let response: ApiSetContactCustomDataResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_set_user_auto_accept_member_contacts(
&self,
command: ApiSetUserAutoAcceptMemberContacts,
) -> impl Future<Output = Result<Arc<CmdOkResponse>, Self::Error>> + Send {
async move {
let response: ApiSetUserAutoAcceptMemberContactsResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn show_active_user(
&self,
) -> impl Future<Output = Result<Arc<ActiveUserResponse>, Self::Error>> + Send {
async move {
let command = ShowActiveUser {};
let response: ShowActiveUserResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn create_active_user(
&self,
new_user: NewUser,
) -> impl Future<Output = Result<Arc<ActiveUserResponse>, Self::Error>> + Send {
async move {
let command = CreateActiveUser { new_user };
let response: CreateActiveUserResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn list_users(
&self,
) -> impl Future<Output = Result<Arc<UsersListResponse>, Self::Error>> + Send {
async move {
let command = ListUsers {};
let response: ListUsersResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_set_active_user(
&self,
command: ApiSetActiveUser,
) -> impl Future<Output = Result<Arc<ActiveUserResponse>, Self::Error>> + Send {
async move {
let response: ApiSetActiveUserResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_delete_user(
&self,
command: ApiDeleteUser,
) -> impl Future<Output = Result<Arc<CmdOkResponse>, Self::Error>> + Send {
async move {
let response: ApiDeleteUserResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn api_update_profile(
&self,
user_id: i64,
profile: Profile,
) -> impl Future<Output = Result<ApiUpdateProfileResponse, Self::Error>> + Send {
async move {
let command = ApiUpdateProfile { user_id, profile };
let response: ApiUpdateProfileResponse = self.send(command).await?;
Ok(response)
}
}
fn api_set_contact_prefs(
&self,
contact_id: i64,
preferences: Preferences,
) -> impl Future<Output = Result<Arc<ContactPrefsUpdatedResponse>, Self::Error>> + Send {
async move {
let command = ApiSetContactPrefs {
contact_id,
preferences,
};
let response: ApiSetContactPrefsResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
fn start_chat(
&self,
command: StartChat,
) -> impl Future<Output = Result<StartChatResponse, Self::Error>> + Send {
async move {
let response: StartChatResponse = self.send(command).await?;
Ok(response)
}
}
fn api_stop_chat(
&self,
) -> impl Future<Output = Result<Arc<ChatStoppedResponse>, Self::Error>> + Send {
async move {
let command = ApiStopChat {};
let response: ApiStopChatResponse = self.send(command).await?;
Ok(response.into_inner())
}
}
}
#[derive(Serialize, Deserialize)]
pub struct WebSocketResponseShape<T> {
pub resp: WebSocketResponseShapeInner<T>,
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum WebSocketResponseShapeInner<T> {
Response(T),
Error(ChatCmdError),
Undocumented(JsonObject),
}
impl<'de, T: 'de + Deserialize<'de>> ExtractResponse<'de, T> for WebSocketResponseShape<T> {
fn extract_response(self) -> Result<T, BadResponseError> {
self.resp.extract_response()
}
}
impl<'de, T: 'de + Deserialize<'de>> ExtractResponse<'de, T> for WebSocketResponseShapeInner<T> {
fn extract_response(self) -> Result<T, BadResponseError> {
match self {
Self::Response(resp) => Ok(resp),
Self::Error(err) => Err(BadResponseError::ChatError(
err.into_inner().chat_error.clone(),
)),
Self::Undocumented(json) => Err(BadResponseError::Undocumented(json)),
}
}
}
#[derive(Serialize, Deserialize)]
pub enum FfiResponseShape<T> {
#[serde(rename = "result")]
Result(T),
#[serde(rename = "error")]
Error(Arc<ChatError>),
#[serde(untagged)]
Undocumented(JsonObject),
}
impl<'de, T: 'de + Deserialize<'de>> ExtractResponse<'de, T> for FfiResponseShape<T> {
fn extract_response(self) -> Result<T, BadResponseError> {
match self {
Self::Result(resp) => Ok(resp),
Self::Error(err) => Err(BadResponseError::ChatError(err)),
Self::Undocumented(json) => Err(BadResponseError::Undocumented(json)),
}
}
}
#[derive(Debug)]
pub enum BadResponseError {
ChatError(Arc<ChatError>),
InvalidJson(serde_json::Error),
Undocumented(JsonObject),
}
impl BadResponseError {
pub fn chat_error(&self) -> Option<&ChatError> {
if let Self::ChatError(error) = self {
Some(error.as_ref())
} else {
None
}
}
pub fn invalid_json(&self) -> Option<&serde_json::Error> {
if let Self::InvalidJson(error) = self {
Some(error)
} else {
None
}
}
pub fn undocumented(&self) -> Option<&JsonObject> {
if let Self::Undocumented(error) = self {
Some(error)
} else {
None
}
}
}
impl std::error::Error for BadResponseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::ChatError(error) => Some(error.as_ref()),
Self::InvalidJson(error) => Some(error),
Self::Undocumented(_) => None,
}
}
}
impl std::fmt::Display for BadResponseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ChatError(resp) => writeln!(f, "Bad response:\n{resp:#}"),
Self::Undocumented(resp) => writeln!(f, "Unexpected response:\n{resp:#}"),
Self::InvalidJson(err) => writeln!(f, "Invalid JSON:\n{err:#}"),
}
}
}
pub enum UndocumentedResponse<T> {
Documented(T),
Undocumented(JsonObject),
}
pub trait AllowUndocumentedResponses<T, E> {
fn allow_undocumented(self) -> Result<UndocumentedResponse<T>, E>;
}
impl<T, E> AllowUndocumentedResponses<T, E> for Result<T, E>
where
E: ClientApiError,
{
fn allow_undocumented(self) -> Result<UndocumentedResponse<T>, E> {
match self {
Ok(resp) => Ok(UndocumentedResponse::Documented(resp)),
Err(mut e) => match e.bad_response_mut() {
Some(BadResponseError::Undocumented(btree_map)) => Ok(
UndocumentedResponse::Undocumented(std::mem::take(btree_map)),
),
_ => Err(e),
},
}
}
}