Initial commit
This commit is contained in:
commit
da78c9bbab
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
2906
Cargo.lock
generated
Normal file
2906
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
39
Cargo.toml
Normal file
39
Cargo.toml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
[package]
|
||||||
|
name = "network"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "network"
|
||||||
|
crate-type = ["staticlib", "cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# uniffi related:
|
||||||
|
uniffi = "0.22.0"
|
||||||
|
uniffi_macros = "0.22.0"
|
||||||
|
|
||||||
|
# actual deps:
|
||||||
|
reqwest = { version = "0.11.13", features = ["json", "blocking"]}
|
||||||
|
chrono = { version = "0.4", features = [ "serde" ] }
|
||||||
|
chrono-tz = { version = "0.8" }
|
||||||
|
thiserror = "1.0.37"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = { version = "1" }
|
||||||
|
bincode = "1.3.3"
|
||||||
|
regex = "1.6.0" # Regular expression utilization
|
||||||
|
|
||||||
|
# net & async:
|
||||||
|
tokio-tungstenite = "0.18.0"
|
||||||
|
tokio = { version = "1.20.1", features = ["full"] }
|
||||||
|
futures = "0.3.25"
|
||||||
|
futures-util = "0.3.26"
|
||||||
|
|
||||||
|
# internal deps
|
||||||
|
err = { git = "https://git.franklinblanco.dev/franklinblanco/err.git" }
|
||||||
|
league-types = { git = "https://git.franklinblanco.dev/franklinblanco/league-types.git" }
|
||||||
|
dev-dtos = { git = "https://git.franklinblanco.dev/franklinblanco/user-svc-dtos-rust.git" }
|
||||||
|
chat-types = { git = "https://git.franklinblanco.dev/franklinblanco/chat-types.git" }
|
||||||
|
chat-communicators = { git = "https://git.franklinblanco.dev/franklinblanco/chat-communicators.git" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
uniffi_build = "0.22.0"
|
4
build.rs
Normal file
4
build.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
fn main() {
|
||||||
|
//set_var("OUT_DIR", "./");
|
||||||
|
uniffi_build::generate_scaffolding("./src/network.udl").unwrap();
|
||||||
|
}
|
BIN
src/.DS_Store
vendored
Normal file
BIN
src/.DS_Store
vendored
Normal file
Binary file not shown.
49
src/callbacks/chat.rs
Normal file
49
src/callbacks/chat.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use std::sync::{Arc};
|
||||||
|
|
||||||
|
use dev_dtos::dtos::user::user_dtos::UserForAuthenticationDto;
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
|
||||||
|
use crate::{utils::storage, ForeignError, client::chat::init_client_connection};
|
||||||
|
|
||||||
|
pub enum ClientError {
|
||||||
|
One, Two, Three
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WebsocketFfi: Send + Sync + std::fmt::Debug {
|
||||||
|
/// Method that rust will call once a message is recieved from the backend
|
||||||
|
/// Swift just needs to know the message info to actually update it.
|
||||||
|
fn message_recieved(&self, message: chat_types::client_types::chat_message::ChatMessage) -> Result<(), ForeignError>;
|
||||||
|
fn message_sent(&self, ) -> Result<(), ForeignError>;
|
||||||
|
fn message_delivered(&self, message: chat_types::client_types::chat_message::ChatMessage) -> Result<(), ForeignError>;
|
||||||
|
fn message_seen(&self, message: chat_types::client_types::chat_message::ChatMessage) -> Result<(), ForeignError>;
|
||||||
|
fn logged_in(&self) -> Result<(), ForeignError>;
|
||||||
|
fn error(&self, error: String) -> Result<(), ForeignError>;
|
||||||
|
fn client_error(&self, error: ClientError) -> Result<(), ForeignError>;
|
||||||
|
//fn attempt(&self, string_from_rust: String) -> Result<Option<String>, ForeignError>;
|
||||||
|
}
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct WebsocketCaller;
|
||||||
|
|
||||||
|
impl<'a> WebsocketCaller {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
WebsocketCaller::default()
|
||||||
|
}
|
||||||
|
/// Method to be called from swift to initiate websocket connection.
|
||||||
|
/// This method being called assumes the following:
|
||||||
|
/// - Valid UserForAuthentication is stored in device storage
|
||||||
|
pub fn init_ws_connection(&'a self, websocket_ffi: Box<dyn WebsocketFfi>) {
|
||||||
|
let ws_ffi_rwlock = Arc::new(websocket_ffi);
|
||||||
|
|
||||||
|
let user: UserForAuthenticationDto = storage::read("user".into()).unwrap(); //TODO: Remove unwrap
|
||||||
|
//websocket_ffi.message_recieved(ChatMessage { id: 20, from_id: 2, to_id: 1, message: "What the fuck nigga".as_bytes().to_vec(), message_content: MessageContentType::Text, time_sent: 1, time_delivered: vec![], time_seen: vec![] }).unwrap();
|
||||||
|
|
||||||
|
let rt = Runtime::new().unwrap();
|
||||||
|
let _ = rt.block_on(
|
||||||
|
init_client_connection(user, ws_ffi_rwlock)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pub fn send_message(&self, ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
1
src/callbacks/mod.rs
Normal file
1
src/callbacks/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod chat;
|
55
src/client/base.rs
Normal file
55
src/client/base.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
|
|
||||||
|
use crate::{RustError, MessageResource};
|
||||||
|
|
||||||
|
pub fn perform_request_without_client_sync<B: Serialize, R: DeserializeOwned>(
|
||||||
|
base_url: String,
|
||||||
|
method: reqwest::Method,
|
||||||
|
path: String,
|
||||||
|
body: Option<B>,
|
||||||
|
expected_status_code: u16,
|
||||||
|
headers: Vec<(String, String)>,
|
||||||
|
) -> Result<R, RustError> {
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
let mut req_incomplete =
|
||||||
|
client.request(method, format!("{url}{path}", url = base_url, path = path));
|
||||||
|
|
||||||
|
for header in headers {
|
||||||
|
req_incomplete = req_incomplete.header(&header.0, &header.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let req_complete = match body {
|
||||||
|
Some(b) => req_incomplete.json(&b),
|
||||||
|
None => req_incomplete.header("content-length", 0),
|
||||||
|
};
|
||||||
|
println!("{:?}", req_complete);
|
||||||
|
match req_complete.send() {
|
||||||
|
// Error handling here
|
||||||
|
Ok(res) => {
|
||||||
|
// Request sent correctly
|
||||||
|
match res.status().as_u16() == expected_status_code {
|
||||||
|
true => {
|
||||||
|
match res.json::<R>() {
|
||||||
|
Ok(resp_dto) => Ok(resp_dto), // Return correctly deserialized obj
|
||||||
|
Err(err) => Err(RustError::SerdeError{ error: MessageResource { key: "COMMUNICATOR.DESERIALIZE_JSON_FAILED".into(), message: Some(err.to_string()) }, serde_error_str: None }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false => {
|
||||||
|
//If status code is any other than expected
|
||||||
|
Err(RustError::UnexpectedStatusCode{
|
||||||
|
expected: expected_status_code,
|
||||||
|
actual: res.status().as_u16(),
|
||||||
|
errors: match res.json::<Vec<MessageResource>>() {
|
||||||
|
Ok(messages) => messages,
|
||||||
|
Err(e) => vec![MessageResource { key: "COMMUNICATOR.NO_ERRORS".into(), message: Some(e.to_string()) }],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Request couldn't be sent
|
||||||
|
Err(RustError::Network{error: MessageResource { key: "COMMUNICATOR.NETWORK".into(), message: Some(e.to_string()) }})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/client/chat/handler.rs
Normal file
25
src/client/chat/handler.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use std::sync::{Arc};
|
||||||
|
|
||||||
|
use chat_types::dto::server_out::ServerMessageOut;
|
||||||
|
use tokio_tungstenite::tungstenite::Message;
|
||||||
|
|
||||||
|
use crate::{WebsocketFfi};
|
||||||
|
|
||||||
|
use super::utils::interpret_message;
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn handle_message(message: Message, ws_caller: &Arc<Box<dyn WebsocketFfi>>) -> Result<(), Box<dyn std::error::Error + Send + Sync>>{
|
||||||
|
let server_message_out = interpret_message(message)?;
|
||||||
|
match server_message_out {
|
||||||
|
ServerMessageOut::Acknowledge => {
|
||||||
|
todo!()
|
||||||
|
},
|
||||||
|
ServerMessageOut::LoggedIn => ws_caller.logged_in()?,
|
||||||
|
ServerMessageOut::MessageSent => ws_caller.message_sent()?,
|
||||||
|
ServerMessageOut::MessageRecieved(message) => ws_caller.message_recieved(message.into())?,
|
||||||
|
ServerMessageOut::MessageDelivered(message) => ws_caller.message_delivered(message.into())?,
|
||||||
|
ServerMessageOut::MessageSeen(message) => ws_caller.message_seen(message.into())?,
|
||||||
|
ServerMessageOut::Error(error) => ws_caller.error(error)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
96
src/client/chat/mod.rs
Normal file
96
src/client/chat/mod.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
pub mod utils; pub mod handler;
|
||||||
|
|
||||||
|
use std::{sync::{RwLock, Arc}};
|
||||||
|
|
||||||
|
use chat_types::{dto::server_in::ServerMessageIn, domain::error::SocketError};
|
||||||
|
use dev_dtos::dtos::user::user_dtos::UserForAuthenticationDto;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tokio_tungstenite::{connect_async};
|
||||||
|
use futures_util::{StreamExt};
|
||||||
|
|
||||||
|
use crate::{WebsocketFfi};
|
||||||
|
|
||||||
|
use self::{utils::send_message, handler::handle_message};
|
||||||
|
|
||||||
|
// TODO: Message queue for sending
|
||||||
|
static _MESSAGE_QUEUE: RwLock<Vec<ServerMessageIn>> = RwLock::new(Vec::new());
|
||||||
|
|
||||||
|
pub async fn init_client_connection(user: UserForAuthenticationDto, ws_caller: Arc<Box<dyn WebsocketFfi>>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let ws_stream = match connect_async("ws://0.0.0.0:3000/websocket").await {
|
||||||
|
Ok((stream, _response)) => {
|
||||||
|
stream
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("WebSocket handshake for server failed with {e}!");
|
||||||
|
return Err(SocketError::boxed_error(format!("WebSocket handshake for server failed with {e}!")));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (sender, mut receiver) = ws_stream.split();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Login
|
||||||
|
//
|
||||||
|
|
||||||
|
//spawn an async sender to push some more messages into the server
|
||||||
|
let caller = ws_caller.clone();
|
||||||
|
//receiver just prints whatever it gets
|
||||||
|
let mut recv_task = tokio::spawn(async move {
|
||||||
|
while let Some(Ok(msg)) = receiver.next().await {
|
||||||
|
// Never break this loop?
|
||||||
|
let a = handle_message(msg, &caller).await;
|
||||||
|
println!("Something happened {:?}", a);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let sender_arc = Arc::new(Mutex::new(sender));
|
||||||
|
send_message(sender_arc.clone(), ServerMessageIn::Login(user)).await?;
|
||||||
|
|
||||||
|
//wait for either task to finish and kill the other task
|
||||||
|
tokio::select! {
|
||||||
|
/*_ = (&mut send_task) => {
|
||||||
|
recv_task.abort();
|
||||||
|
},*/
|
||||||
|
_ = (&mut recv_task) => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
/// Function to handle messages we get (with a slight twist that Frame variant is visible
|
||||||
|
/// since we are working with the underlying tungstenite library directly without axum here).
|
||||||
|
fn process_message(msg: Message) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
match msg {
|
||||||
|
Message::Text(t) => {
|
||||||
|
println!(">>> got str: {:?}", t);
|
||||||
|
}
|
||||||
|
Message::Binary(d) => {
|
||||||
|
println!(">>> got {} bytes: {:?}", d.len(), d);
|
||||||
|
}
|
||||||
|
Message::Close(c) => {
|
||||||
|
if let Some(cf) = c {
|
||||||
|
println!(
|
||||||
|
">>> got close with code {} and reason `{}`",
|
||||||
|
cf.code, cf.reason
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!(">>> somehow got close message without CloseFrame");
|
||||||
|
}
|
||||||
|
return ControlFlow::Break(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::Pong(v) => {
|
||||||
|
println!(">>> got pong with {:?}", v);
|
||||||
|
}
|
||||||
|
// Just as with axum server, the underlying tungstenite websocket library
|
||||||
|
// will handle Ping for you automagically by replying with Pong and copying the
|
||||||
|
// v according to spec. But if you need the contents of the pings you can see them here.
|
||||||
|
Message::Ping(v) => {
|
||||||
|
println!(">>> got ping with {:?}", v);
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::Frame(_) => {
|
||||||
|
unreachable!("This is never supposed to happen")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}*/
|
||||||
|
|
43
src/client/chat/utils.rs
Normal file
43
src/client/chat/utils.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use chat_types::{dto::{server_in::{ServerMessageIn, Receivable}, server_out::{ServerMessageOut, Sendable}, message::ClientMessage}, domain::error::SocketError};
|
||||||
|
use futures::{SinkExt};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tokio_tungstenite::{tungstenite::{Message}};
|
||||||
|
|
||||||
|
|
||||||
|
/// Este es el metodo para enviar mensajes a un cliente a traves de un websocket
|
||||||
|
/// Si le pasas un None en el payload tienes que darle un tipo al metodo, ya que
|
||||||
|
/// El compilador no permite especificarle un metodo default.
|
||||||
|
pub async fn send_message<S>(
|
||||||
|
sender: Arc<Mutex<S>>,
|
||||||
|
message: ServerMessageIn,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> where S: futures_util::Sink<tokio_tungstenite::tungstenite::Message> + Unpin{
|
||||||
|
Ok(
|
||||||
|
match sender
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.send(Message::Text(serde_json::to_string(
|
||||||
|
&message.into_message()?,
|
||||||
|
)?))
|
||||||
|
.await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_) => return Err(SocketError::boxed_error("Message couldn't be sent for some weird reason...")),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// use this function to convert a Message::Text() from a client socket connection
|
||||||
|
/// into a ClientMessage<Payload>
|
||||||
|
pub fn interpret_message(
|
||||||
|
message: Message,
|
||||||
|
) -> Result<ServerMessageOut, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
if let Message::Text(txt) = message {
|
||||||
|
// txt should be a {"type": "SOMETHING"} or a {"type": "SOMETHING", "payload": {}}
|
||||||
|
let client_message: ClientMessage = serde_json::from_str(txt.as_str())?; //Add error message?
|
||||||
|
Ok(ServerMessageOut::from_message(client_message)?)
|
||||||
|
} else {
|
||||||
|
Err(SocketError::boxed_error(
|
||||||
|
"Recieved client Message is not of type Text...",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
118
src/client/league/mod.rs
Normal file
118
src/client/league/mod.rs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
use dev_dtos::{domain::user::token::Token, dtos::user::user_dtos::{UserForLoginDto, UserForAuthenticationDto}};
|
||||||
|
use league_types::{domain::{sport::Sport, player::Player, league::League, place::Place, league_player::LeaguePlayer, enums::league_player_status::LeaguePlayerStatus, trust::Trust}, dto::{player::{PlayerForCreationDto, PlayerForUpdateDto, PlayerProfileDto}, league::LeagueForCreationDto, league_player::JoinRequest, trust::TrustRequestDto}};
|
||||||
|
|
||||||
|
use reqwest::Method;
|
||||||
|
|
||||||
|
use crate::{client::base::perform_request_without_client_sync as perform_request, RustError};
|
||||||
|
|
||||||
|
pub const BASE_URL: &str = "http://backend.blancoinfante.com/";
|
||||||
|
|
||||||
|
// #############
|
||||||
|
// SPORT ROUTES
|
||||||
|
// #############
|
||||||
|
pub fn get_all_sports() -> Result<Vec<Sport>, RustError> {
|
||||||
|
perform_request::<(), Vec<Sport>>(BASE_URL.to_string(), Method::GET, "league/sport".into(), None, 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
// #############
|
||||||
|
// PLAYER ROUTES
|
||||||
|
// #############
|
||||||
|
pub fn create_player_profile(player: PlayerForCreationDto) -> Result<Token, RustError> {
|
||||||
|
perform_request::<PlayerForCreationDto, Token>(BASE_URL.to_string(), Method::POST, "league/player".into(), Some(player), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn edit_player_profile(player: PlayerForUpdateDto) -> Result<Player, RustError> {
|
||||||
|
perform_request::<PlayerForUpdateDto, Player>(BASE_URL.to_string(), Method::PUT, "league/player".into(), Some(player), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn login(user: UserForLoginDto) -> Result<Token, RustError> {
|
||||||
|
perform_request::<UserForLoginDto, Token>(BASE_URL.to_string(), Method::POST, "league/player/login".into(), Some(user), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_player_profile(player_id: u32) -> Result<PlayerProfileDto, RustError> {
|
||||||
|
perform_request::<(), PlayerProfileDto>(BASE_URL.to_string(), Method::GET, format!("league/player/profile/{}", player_id), None, 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_player_trusted_list(player_id: u32) -> Result<Vec<Player>, RustError> {
|
||||||
|
perform_request::<(), Vec<Player>>(BASE_URL.to_string(), Method::GET, format!("league/player/trusted_by/{}", player_id), None, 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
// #############
|
||||||
|
// LEAGUE ROUTES
|
||||||
|
// #############
|
||||||
|
pub fn create_league(league: LeagueForCreationDto) -> Result<League, RustError> {
|
||||||
|
perform_request::<LeagueForCreationDto, League>(BASE_URL.to_string(), Method::POST, "league/league".into(), Some(league), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_open_leagues_in_my_area(page: u16, user: UserForAuthenticationDto) -> Result<Vec<League>, RustError> {
|
||||||
|
perform_request::<UserForAuthenticationDto, Vec<League>>(BASE_URL.to_string(), Method::POST, format!("league/league/nearme/{}", page), Some(user), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_leagues_in_my_country(country: String, page: u16) -> Result<Vec<League>, RustError> {
|
||||||
|
perform_request::<(), Vec<League>>(BASE_URL.to_string(), Method::GET, format!("league/league/{}/{}", country, page), None, 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_specific_league(league_id: u32) -> Result<League, RustError> {
|
||||||
|
perform_request::<(), League>(BASE_URL.to_string(), Method::GET, format!("league/league/{}", league_id), None, 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_leagues_hosted_by_player(user: UserForAuthenticationDto, player_id: u32, page: u16) -> Result<Vec<League>, RustError> {
|
||||||
|
perform_request::<UserForAuthenticationDto, Vec<League>>(BASE_URL.to_string(), Method::POST, format!("league/league/player/{}/{}", player_id, page), Some(user), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_leagues_in_place(place_id: u32, page: u16) -> Result<Vec<League>, RustError> {
|
||||||
|
perform_request::<(), Vec<League>>(BASE_URL.to_string(), Method::GET, format!("league/league/place/{}/{}", place_id, page), None, 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_average_league_age(user: UserForAuthenticationDto, league_id: u32) -> Result<u8, RustError> {
|
||||||
|
perform_request::<UserForAuthenticationDto, u8>(BASE_URL.to_string(), Method::POST, format!("league/league/{}/age", league_id), Some(user), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
// #############
|
||||||
|
// PLACE ROUTES
|
||||||
|
// #############
|
||||||
|
pub fn get_places_for_country(country: String, page: u16) -> Result<Vec<Place>, RustError> {
|
||||||
|
perform_request::<(), Vec<Place>>(BASE_URL.to_string(), Method::GET, format!("league/place/country/{}/page/{}", country, page), None, 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_places_for_sport(sport_id: u32, page: u16) -> Result<Vec<Place>, RustError> {
|
||||||
|
perform_request::<(), Vec<Place>>(BASE_URL.to_string(), Method::GET, format!("league/place/sport/{}/page/{}", sport_id, page), None, 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_places_in_my_area(user: UserForAuthenticationDto, page: u16) -> Result<Vec<Place>, RustError> {
|
||||||
|
perform_request::<UserForAuthenticationDto, Vec<Place>>(BASE_URL.to_string(), Method::POST, format!("league/place/nearme/{}", page), Some(user), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
// ####################
|
||||||
|
// LEAGUE_PLAYER ROUTES
|
||||||
|
// ####################
|
||||||
|
pub fn request_to_join_league(join_req: JoinRequest) -> Result<LeaguePlayer, RustError> {
|
||||||
|
perform_request::<JoinRequest, LeaguePlayer>(BASE_URL.to_string(), Method::POST, "league/league_player/request".into(), Some(join_req), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_league_request_status(join_req: JoinRequest) -> Result<LeaguePlayer, RustError> {
|
||||||
|
perform_request::<JoinRequest, LeaguePlayer>(BASE_URL.to_string(), Method::POST, "league/league_player/request/status".into(), Some(join_req), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_league_request_status(status: LeaguePlayerStatus, join_req: JoinRequest) -> Result<LeaguePlayer, RustError> {
|
||||||
|
perform_request::<JoinRequest, LeaguePlayer>(BASE_URL.to_string(), Method::PUT, format!("league/league_player/request/{}", status), Some(join_req), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_all_leagues_player_has_applied_to(join_req: JoinRequest, page: u16) -> Result<Vec<League>, RustError> {
|
||||||
|
perform_request::<JoinRequest, Vec<League>>(BASE_URL.to_string(), Method::POST, format!("league/league_player/leagues/{}", page), Some(join_req), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_all_players_in_league(join_req: JoinRequest) -> Result<Vec<Player>, RustError> {
|
||||||
|
perform_request::<JoinRequest, Vec<Player>>(BASE_URL.to_string(), Method::POST, "league/league_player/players".into(), Some(join_req), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
// #############
|
||||||
|
// TRUST ROUTES
|
||||||
|
// #############
|
||||||
|
pub fn add_trusted_player(trust_req: TrustRequestDto) -> Result<Trust, RustError> {
|
||||||
|
perform_request::<TrustRequestDto, Trust>(BASE_URL.to_string(), Method::POST, "league/trust".into(), Some(trust_req), 200, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_trusted_player(trust_req: TrustRequestDto) -> Result<Trust, RustError> {
|
||||||
|
perform_request::<TrustRequestDto, Trust>(BASE_URL.to_string(), Method::DELETE, "league/trust".into(), Some(trust_req), 200, vec![])
|
||||||
|
}
|
3
src/client/mod.rs
Normal file
3
src/client/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod league;
|
||||||
|
pub mod base;
|
||||||
|
pub mod chat;
|
34
src/lib.rs
Normal file
34
src/lib.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
uniffi_macros::include_scaffolding!("network");
|
||||||
|
|
||||||
|
pub mod types;
|
||||||
|
pub mod client;
|
||||||
|
pub mod utils;
|
||||||
|
pub mod callbacks;
|
||||||
|
|
||||||
|
pub use chat_types::client_types::chat_room::ChatRoom;
|
||||||
|
pub use dev_dtos::dtos::user::user_dtos::UserForAuthenticationDto;
|
||||||
|
use utils::storage;
|
||||||
|
pub use utils::storage::*;
|
||||||
|
pub use callbacks::chat::*;
|
||||||
|
|
||||||
|
pub use league_types::domain::sport::Sport;
|
||||||
|
pub use types::error::*;
|
||||||
|
//pub use chat_communicators::client::chat::*;
|
||||||
|
pub use chat_types::client_types::chat_message::*;
|
||||||
|
|
||||||
|
pub fn get_all_sports() -> Result<Vec<Sport>, RustError> {
|
||||||
|
client::league::get_all_sports()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_me() -> Result<UserForAuthenticationDto, RustError> {
|
||||||
|
storage::read("user".into())
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! unwrap_rust_error {
|
||||||
|
($e:expr) => {
|
||||||
|
match $e {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(error) => return Err(error.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
373
src/network.swift
Normal file
373
src/network.swift
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
// This file was autogenerated by some hot garbage in the `uniffi` crate.
|
||||||
|
// Trust me, you don't want to mess with it!
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// Depending on the consumer's build setup, the low-level FFI code
|
||||||
|
// might be in a separate module, or it might be compiled inline into
|
||||||
|
// this module. This is a bit of light hackery to work with both.
|
||||||
|
#if canImport(networkFFI)
|
||||||
|
import networkFFI
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fileprivate extension RustBuffer {
|
||||||
|
// Allocate a new buffer, copying the contents of a `UInt8` array.
|
||||||
|
init(bytes: [UInt8]) {
|
||||||
|
let rbuf = bytes.withUnsafeBufferPointer { ptr in
|
||||||
|
RustBuffer.from(ptr)
|
||||||
|
}
|
||||||
|
self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func from(_ ptr: UnsafeBufferPointer<UInt8>) -> RustBuffer {
|
||||||
|
try! rustCall { ffi_network_a3fa_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees the buffer in place.
|
||||||
|
// The buffer must not be used after this is called.
|
||||||
|
func deallocate() {
|
||||||
|
try! rustCall { ffi_network_a3fa_rustbuffer_free(self, $0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate extension ForeignBytes {
|
||||||
|
init(bufferPointer: UnsafeBufferPointer<UInt8>) {
|
||||||
|
self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For every type used in the interface, we provide helper methods for conveniently
|
||||||
|
// lifting and lowering that type from C-compatible data, and for reading and writing
|
||||||
|
// values of that type in a buffer.
|
||||||
|
|
||||||
|
// Helper classes/extensions that don't change.
|
||||||
|
// Someday, this will be in a libray of its own.
|
||||||
|
|
||||||
|
fileprivate extension Data {
|
||||||
|
init(rustBuffer: RustBuffer) {
|
||||||
|
// TODO: This copies the buffer. Can we read directly from a
|
||||||
|
// Rust buffer?
|
||||||
|
self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper class to read values out of a byte buffer.
|
||||||
|
fileprivate class Reader {
|
||||||
|
let data: Data
|
||||||
|
var offset: Data.Index
|
||||||
|
|
||||||
|
init(data: Data) {
|
||||||
|
self.data = data
|
||||||
|
self.offset = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads an integer at the current offset, in big-endian order, and advances
|
||||||
|
// the offset on success. Throws if reading the integer would move the
|
||||||
|
// offset past the end of the buffer.
|
||||||
|
func readInt<T: FixedWidthInteger>() throws -> T {
|
||||||
|
let range = offset..<offset + MemoryLayout<T>.size
|
||||||
|
guard data.count >= range.upperBound else {
|
||||||
|
throw UniffiInternalError.bufferOverflow
|
||||||
|
}
|
||||||
|
if T.self == UInt8.self {
|
||||||
|
let value = data[offset]
|
||||||
|
offset += 1
|
||||||
|
return value as! T
|
||||||
|
}
|
||||||
|
var value: T = 0
|
||||||
|
let _ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0, from: range)})
|
||||||
|
offset = range.upperBound
|
||||||
|
return value.bigEndian
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads an arbitrary number of bytes, to be used to read
|
||||||
|
// raw bytes, this is useful when lifting strings
|
||||||
|
func readBytes(count: Int) throws -> Array<UInt8> {
|
||||||
|
let range = offset..<(offset+count)
|
||||||
|
guard data.count >= range.upperBound else {
|
||||||
|
throw UniffiInternalError.bufferOverflow
|
||||||
|
}
|
||||||
|
var value = [UInt8](repeating: 0, count: count)
|
||||||
|
value.withUnsafeMutableBufferPointer({ buffer in
|
||||||
|
data.copyBytes(to: buffer, from: range)
|
||||||
|
})
|
||||||
|
offset = range.upperBound
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a float at the current offset.
|
||||||
|
@inlinable
|
||||||
|
func readFloat() throws -> Float {
|
||||||
|
return Float(bitPattern: try readInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a float at the current offset.
|
||||||
|
@inlinable
|
||||||
|
func readDouble() throws -> Double {
|
||||||
|
return Double(bitPattern: try readInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicates if the offset has reached the end of the buffer.
|
||||||
|
@inlinable
|
||||||
|
func hasRemaining() -> Bool {
|
||||||
|
return offset < data.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper class to write values into a byte buffer.
|
||||||
|
fileprivate class Writer {
|
||||||
|
var bytes: [UInt8]
|
||||||
|
var offset: Array<UInt8>.Index
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.bytes = []
|
||||||
|
self.offset = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeBytes<S>(_ byteArr: S) where S: Sequence, S.Element == UInt8 {
|
||||||
|
bytes.append(contentsOf: byteArr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes an integer in big-endian order.
|
||||||
|
//
|
||||||
|
// Warning: make sure what you are trying to write
|
||||||
|
// is in the correct type!
|
||||||
|
func writeInt<T: FixedWidthInteger>(_ value: T) {
|
||||||
|
var value = value.bigEndian
|
||||||
|
withUnsafeBytes(of: &value) { bytes.append(contentsOf: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
func writeFloat(_ value: Float) {
|
||||||
|
writeInt(value.bitPattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
func writeDouble(_ value: Double) {
|
||||||
|
writeInt(value.bitPattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocol for types that transfer other types across the FFI. This is
|
||||||
|
// analogous go the Rust trait of the same name.
|
||||||
|
fileprivate protocol FfiConverter {
|
||||||
|
associatedtype FfiType
|
||||||
|
associatedtype SwiftType
|
||||||
|
|
||||||
|
static func lift(_ value: FfiType) throws -> SwiftType
|
||||||
|
static func lower(_ value: SwiftType) -> FfiType
|
||||||
|
static func read(from buf: Reader) throws -> SwiftType
|
||||||
|
static func write(_ value: SwiftType, into buf: Writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Types conforming to `Primitive` pass themselves directly over the FFI.
|
||||||
|
fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { }
|
||||||
|
|
||||||
|
extension FfiConverterPrimitive {
|
||||||
|
static func lift(_ value: FfiType) throws -> SwiftType {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
static func lower(_ value: SwiftType) -> FfiType {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
|
||||||
|
// Used for complex types where it's hard to write a custom lift/lower.
|
||||||
|
fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}
|
||||||
|
|
||||||
|
extension FfiConverterRustBuffer {
|
||||||
|
static func lift(_ buf: RustBuffer) throws -> SwiftType {
|
||||||
|
let reader = Reader(data: Data(rustBuffer: buf))
|
||||||
|
let value = try read(from: reader)
|
||||||
|
if reader.hasRemaining() {
|
||||||
|
throw UniffiInternalError.incompleteData
|
||||||
|
}
|
||||||
|
buf.deallocate()
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
static func lower(_ value: SwiftType) -> RustBuffer {
|
||||||
|
let writer = Writer()
|
||||||
|
write(value, into: writer)
|
||||||
|
return RustBuffer(bytes: writer.bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// An error type for FFI errors. These errors occur at the UniFFI level, not
|
||||||
|
// the library level.
|
||||||
|
fileprivate enum UniffiInternalError: LocalizedError {
|
||||||
|
case bufferOverflow
|
||||||
|
case incompleteData
|
||||||
|
case unexpectedOptionalTag
|
||||||
|
case unexpectedEnumCase
|
||||||
|
case unexpectedNullPointer
|
||||||
|
case unexpectedRustCallStatusCode
|
||||||
|
case unexpectedRustCallError
|
||||||
|
case unexpectedStaleHandle
|
||||||
|
case rustPanic(_ message: String)
|
||||||
|
|
||||||
|
public var errorDescription: String? {
|
||||||
|
switch self {
|
||||||
|
case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
|
||||||
|
case .incompleteData: return "The buffer still has data after lifting its containing value"
|
||||||
|
case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
|
||||||
|
case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
|
||||||
|
case .unexpectedNullPointer: return "Raw pointer value was null"
|
||||||
|
case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
|
||||||
|
case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
|
||||||
|
case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
|
||||||
|
case let .rustPanic(message): return message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate let CALL_SUCCESS: Int8 = 0
|
||||||
|
fileprivate let CALL_ERROR: Int8 = 1
|
||||||
|
fileprivate let CALL_PANIC: Int8 = 2
|
||||||
|
|
||||||
|
fileprivate extension RustCallStatus {
|
||||||
|
init() {
|
||||||
|
self.init(
|
||||||
|
code: CALL_SUCCESS,
|
||||||
|
errorBuf: RustBuffer.init(
|
||||||
|
capacity: 0,
|
||||||
|
len: 0,
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func rustCall<T>(_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
|
||||||
|
try makeRustCall(callback, errorHandler: {
|
||||||
|
$0.deallocate()
|
||||||
|
return UniffiInternalError.unexpectedRustCallError
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private func rustCallWithError<T, F: FfiConverter>
|
||||||
|
(_ errorFfiConverter: F.Type, _ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T
|
||||||
|
where F.SwiftType: Error, F.FfiType == RustBuffer
|
||||||
|
{
|
||||||
|
try makeRustCall(callback, errorHandler: { return try errorFfiConverter.lift($0) })
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makeRustCall<T>(_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T, errorHandler: (RustBuffer) throws -> Error) throws -> T {
|
||||||
|
var callStatus = RustCallStatus.init()
|
||||||
|
let returnedVal = callback(&callStatus)
|
||||||
|
switch callStatus.code {
|
||||||
|
case CALL_SUCCESS:
|
||||||
|
return returnedVal
|
||||||
|
|
||||||
|
case CALL_ERROR:
|
||||||
|
throw try errorHandler(callStatus.errorBuf)
|
||||||
|
|
||||||
|
case CALL_PANIC:
|
||||||
|
// When the rust code sees a panic, it tries to construct a RustBuffer
|
||||||
|
// with the message. But if that code panics, then it just sends back
|
||||||
|
// an empty buffer.
|
||||||
|
if callStatus.errorBuf.len > 0 {
|
||||||
|
throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf))
|
||||||
|
} else {
|
||||||
|
callStatus.errorBuf.deallocate()
|
||||||
|
throw UniffiInternalError.rustPanic("Rust panic")
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw UniffiInternalError.unexpectedRustCallStatusCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public interface members begin here.
|
||||||
|
|
||||||
|
|
||||||
|
fileprivate struct FfiConverterUInt8: FfiConverterPrimitive {
|
||||||
|
typealias FfiType = UInt8
|
||||||
|
typealias SwiftType = UInt8
|
||||||
|
|
||||||
|
static func read(from buf: Reader) throws -> UInt8 {
|
||||||
|
return try lift(buf.readInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
static func write(_ value: UInt8, into buf: Writer) {
|
||||||
|
buf.writeInt(lower(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate struct FfiConverterInt32: FfiConverterPrimitive {
|
||||||
|
typealias FfiType = Int32
|
||||||
|
typealias SwiftType = Int32
|
||||||
|
|
||||||
|
static func read(from buf: Reader) throws -> Int32 {
|
||||||
|
return try lift(buf.readInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
static func write(_ value: Int32, into buf: Writer) {
|
||||||
|
buf.writeInt(lower(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate struct FfiConverterString: FfiConverter {
|
||||||
|
typealias SwiftType = String
|
||||||
|
typealias FfiType = RustBuffer
|
||||||
|
|
||||||
|
static func lift(_ value: RustBuffer) throws -> String {
|
||||||
|
defer {
|
||||||
|
value.deallocate()
|
||||||
|
}
|
||||||
|
if value.data == nil {
|
||||||
|
return String()
|
||||||
|
}
|
||||||
|
let bytes = UnsafeBufferPointer<UInt8>(start: value.data!, count: Int(value.len))
|
||||||
|
return String(bytes: bytes, encoding: String.Encoding.utf8)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static func lower(_ value: String) -> RustBuffer {
|
||||||
|
return value.utf8CString.withUnsafeBufferPointer { ptr in
|
||||||
|
// The swift string gives us int8_t, we want uint8_t.
|
||||||
|
ptr.withMemoryRebound(to: UInt8.self) { ptr in
|
||||||
|
// The swift string gives us a trailing null byte, we don't want it.
|
||||||
|
let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
|
||||||
|
return RustBuffer.from(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func read(from buf: Reader) throws -> String {
|
||||||
|
let len: Int32 = try buf.readInt()
|
||||||
|
return String(bytes: try buf.readBytes(count: Int(len)), encoding: String.Encoding.utf8)!
|
||||||
|
}
|
||||||
|
|
||||||
|
static func write(_ value: String, into buf: Writer) {
|
||||||
|
let len = Int32(value.utf8.count)
|
||||||
|
buf.writeInt(len)
|
||||||
|
buf.writeBytes(value.utf8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func `deez`(`ab`: Int32) -> UInt8 {
|
||||||
|
return try! FfiConverterUInt8.lift(
|
||||||
|
try!
|
||||||
|
|
||||||
|
rustCall() {
|
||||||
|
|
||||||
|
network_a3fa_deez(
|
||||||
|
FfiConverterInt32.lower(`ab`), $0)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Top level initializers and tear down methods.
|
||||||
|
*
|
||||||
|
* This is generated by uniffi.
|
||||||
|
*/
|
||||||
|
public enum NetworkLifecycle {
|
||||||
|
/**
|
||||||
|
* Initialize the FFI and Rust library. This should be only called once per application.
|
||||||
|
*/
|
||||||
|
func initialize() {
|
||||||
|
}
|
||||||
|
}
|
89
src/network.udl
Normal file
89
src/network.udl
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
dictionary MessageResource {
|
||||||
|
string key;
|
||||||
|
string? message;
|
||||||
|
};
|
||||||
|
dictionary Sport {
|
||||||
|
u32 id;
|
||||||
|
string name;
|
||||||
|
u32 category_id;
|
||||||
|
};
|
||||||
|
[Error]
|
||||||
|
enum RustError {
|
||||||
|
"Network",
|
||||||
|
"UnexpectedStatusCode",
|
||||||
|
"SerdeError",
|
||||||
|
"IO",
|
||||||
|
"Uknown",
|
||||||
|
};
|
||||||
|
[Error]
|
||||||
|
enum ForeignError {
|
||||||
|
"FfiCallbackError"
|
||||||
|
};
|
||||||
|
dictionary UserForAuthenticationDto {
|
||||||
|
string app;
|
||||||
|
string id;
|
||||||
|
string token;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary TimeSensitiveAction {
|
||||||
|
u32 by;
|
||||||
|
i64 time;
|
||||||
|
};
|
||||||
|
enum MessageContentType {
|
||||||
|
"Text",
|
||||||
|
"Image",
|
||||||
|
"Audio",
|
||||||
|
"Video",
|
||||||
|
};
|
||||||
|
dictionary ChatMessage {
|
||||||
|
u32 id;
|
||||||
|
u32 from_id;
|
||||||
|
u32 to_id;
|
||||||
|
sequence<u8> message;
|
||||||
|
MessageContentType message_content;
|
||||||
|
i64 time_sent;
|
||||||
|
sequence<TimeSensitiveAction> time_delivered;
|
||||||
|
sequence<TimeSensitiveAction> time_seen;
|
||||||
|
};
|
||||||
|
dictionary ChatRoom {
|
||||||
|
u32 id;
|
||||||
|
string title;
|
||||||
|
u32 owner_id;
|
||||||
|
i64 time_created;
|
||||||
|
i64 last_updated;
|
||||||
|
u64 session_messages;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ClientError {
|
||||||
|
"One",
|
||||||
|
"Two",
|
||||||
|
"Three",
|
||||||
|
};
|
||||||
|
|
||||||
|
callback interface WebsocketFfi {
|
||||||
|
[Throws=ForeignError]
|
||||||
|
void message_recieved(ChatMessage message);
|
||||||
|
[Throws=ForeignError]
|
||||||
|
void logged_in();
|
||||||
|
[Throws=ForeignError]
|
||||||
|
void message_seen(ChatMessage message);
|
||||||
|
[Throws=ForeignError]
|
||||||
|
void message_delivered(ChatMessage message);
|
||||||
|
[Throws=ForeignError]
|
||||||
|
void message_sent();
|
||||||
|
[Throws=ForeignError]
|
||||||
|
void error(string error);
|
||||||
|
[Throws=ForeignError]
|
||||||
|
void client_error(ClientError error);
|
||||||
|
};
|
||||||
|
interface WebsocketCaller {
|
||||||
|
constructor();
|
||||||
|
void init_ws_connection(WebsocketFfi websocket_ffi);
|
||||||
|
};
|
||||||
|
namespace network {
|
||||||
|
[Throws=RustError]
|
||||||
|
UserForAuthenticationDto get_me();
|
||||||
|
[Throws=RustError]
|
||||||
|
sequence<Sport> get_all_sports();
|
||||||
|
void init_storage(string path);
|
||||||
|
};
|
68
src/networkFFI.h
Normal file
68
src/networkFFI.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// This file was autogenerated by some hot garbage in the `uniffi` crate.
|
||||||
|
// Trust me, you don't want to mess with it!
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// The following structs are used to implement the lowest level
|
||||||
|
// of the FFI, and thus useful to multiple uniffied crates.
|
||||||
|
// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H.
|
||||||
|
#ifdef UNIFFI_SHARED_H
|
||||||
|
// We also try to prevent mixing versions of shared uniffi header structs.
|
||||||
|
// If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V4
|
||||||
|
#ifndef UNIFFI_SHARED_HEADER_V4
|
||||||
|
#error Combining helper code from multiple versions of uniffi is not supported
|
||||||
|
#endif // ndef UNIFFI_SHARED_HEADER_V4
|
||||||
|
#else
|
||||||
|
#define UNIFFI_SHARED_H
|
||||||
|
#define UNIFFI_SHARED_HEADER_V4
|
||||||
|
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
|
||||||
|
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
|
||||||
|
|
||||||
|
typedef struct RustBuffer
|
||||||
|
{
|
||||||
|
int32_t capacity;
|
||||||
|
int32_t len;
|
||||||
|
uint8_t *_Nullable data;
|
||||||
|
} RustBuffer;
|
||||||
|
|
||||||
|
typedef int32_t (*ForeignCallback)(uint64_t, int32_t, RustBuffer, RustBuffer *_Nonnull);
|
||||||
|
|
||||||
|
typedef struct ForeignBytes
|
||||||
|
{
|
||||||
|
int32_t len;
|
||||||
|
const uint8_t *_Nullable data;
|
||||||
|
} ForeignBytes;
|
||||||
|
|
||||||
|
// Error definitions
|
||||||
|
typedef struct RustCallStatus {
|
||||||
|
int8_t code;
|
||||||
|
RustBuffer errorBuf;
|
||||||
|
} RustCallStatus;
|
||||||
|
|
||||||
|
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
|
||||||
|
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
|
||||||
|
#endif // def UNIFFI_SHARED_H
|
||||||
|
|
||||||
|
uint8_t network_a3fa_deez(
|
||||||
|
int32_t ab,
|
||||||
|
RustCallStatus *_Nonnull out_status
|
||||||
|
);
|
||||||
|
RustBuffer ffi_network_a3fa_rustbuffer_alloc(
|
||||||
|
int32_t size,
|
||||||
|
RustCallStatus *_Nonnull out_status
|
||||||
|
);
|
||||||
|
RustBuffer ffi_network_a3fa_rustbuffer_from_bytes(
|
||||||
|
ForeignBytes bytes,
|
||||||
|
RustCallStatus *_Nonnull out_status
|
||||||
|
);
|
||||||
|
void ffi_network_a3fa_rustbuffer_free(
|
||||||
|
RustBuffer buf,
|
||||||
|
RustCallStatus *_Nonnull out_status
|
||||||
|
);
|
||||||
|
RustBuffer ffi_network_a3fa_rustbuffer_reserve(
|
||||||
|
RustBuffer buf,int32_t additional,
|
||||||
|
RustCallStatus *_Nonnull out_status
|
||||||
|
);
|
6
src/networkFFI.modulemap
Normal file
6
src/networkFFI.modulemap
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// This file was autogenerated by some hot garbage in the `uniffi` crate.
|
||||||
|
// Trust me, you don't want to mess with it!
|
||||||
|
module networkFFI {
|
||||||
|
header "networkFFI.h"
|
||||||
|
export *
|
||||||
|
}
|
64
src/types/error.rs
Normal file
64
src/types/error.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Serialize, Deserialize, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct MessageResource {
|
||||||
|
pub key: String,
|
||||||
|
pub message: Option<String>
|
||||||
|
}
|
||||||
|
impl Display for MessageResource {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "MessageResource: Key: {}, Error: {:?}", self.key, self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum RustError {
|
||||||
|
#[error("Network Error {error}")]
|
||||||
|
Network{error: MessageResource},
|
||||||
|
#[error("UnexpectedStatusCode expected: {expected}, actual: {actual}, errors: {:?}", errors)]
|
||||||
|
UnexpectedStatusCode{expected: u16, actual: u16, errors: Vec<MessageResource>},
|
||||||
|
#[error("SerdeError Error {error}, attempted to SerdeError string: {:?}", serde_error_str)]
|
||||||
|
SerdeError{error: MessageResource, serde_error_str: Option<String>},
|
||||||
|
#[error("IOError Error {error} ")]
|
||||||
|
IO{error: MessageResource},
|
||||||
|
#[error("Uknown error")]
|
||||||
|
Uknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ForeignError {
|
||||||
|
#[error("FFiCallbackError")]
|
||||||
|
FfiCallbackError,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<err::Error> for RustError {
|
||||||
|
fn from(value: err::Error) -> Self {
|
||||||
|
match value {
|
||||||
|
err::Error::Network(msg) => RustError::Network { error: msg.into() },
|
||||||
|
err::Error::IO(msg) => RustError::IO { error: msg.into() },
|
||||||
|
err::Error::UnexpectedStatusCode(expected, actual, messages) => RustError::UnexpectedStatusCode { expected, actual, errors: messages.into_iter().map(|message| message.into()).collect::<Vec<crate::MessageResource>>() },
|
||||||
|
err::Error::Serde(msg) => RustError::SerdeError { error: msg.into(), serde_error_str: None },
|
||||||
|
_ => RustError::Uknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convert_error(error: err::Error) -> RustError {
|
||||||
|
error.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<err::MessageResource> for crate::types::error::MessageResource {
|
||||||
|
fn from(value: err::MessageResource) -> Self {
|
||||||
|
Self { key: match value.key {
|
||||||
|
Some(key) => key,
|
||||||
|
None => "".into(),
|
||||||
|
}, message: Some(value.message) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<uniffi::UnexpectedUniFFICallbackError> for ForeignError {
|
||||||
|
fn from(_value: uniffi::UnexpectedUniFFICallbackError) -> Self {
|
||||||
|
Self::FfiCallbackError
|
||||||
|
}
|
||||||
|
}
|
1
src/types/mod.rs
Normal file
1
src/types/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod error;
|
1
src/utils/mod.rs
Normal file
1
src/utils/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod storage;
|
41
src/utils/storage.rs
Normal file
41
src/utils/storage.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
use std::{sync::RwLock, fs};
|
||||||
|
|
||||||
|
use dev_dtos::dtos::user::user_dtos::UserForAuthenticationDto;
|
||||||
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
|
|
||||||
|
use crate::{RustError, MessageResource};
|
||||||
|
|
||||||
|
static STORAGE_PATH: RwLock<String> = RwLock::new(String::new());
|
||||||
|
|
||||||
|
/// This function MUST BE CALLED on startup or else Storage won't work at all
|
||||||
|
pub fn init_storage(path: String) {
|
||||||
|
let mut write = STORAGE_PATH.write().expect("FATAL ERROR. FAILED TO SECURE A RWLOCK CORRECTLY. STORAGE WON'T WORK NOW.");
|
||||||
|
*write = path;
|
||||||
|
drop(write);
|
||||||
|
// TESTING PURPOSES
|
||||||
|
store(UserForAuthenticationDto{ app: "".into(), id: "3".into(), token: "/2uuNJG3Z2bT9VVd64xBeACPxg64GicloiXtG9uO87as5q5g46TtNu0sAVTACyR8R8uMVXoTBlBP4Q3JhcGB2Q==".to_string() }, "user".into()).unwrap();
|
||||||
|
println!("Wrote shit");
|
||||||
|
}
|
||||||
|
|
||||||
|
// These functions won't be exposed to the clients, just used by the functions they call internally
|
||||||
|
|
||||||
|
pub fn store<T: Serialize>(ty: T, path: String) -> Result<(), RustError>{
|
||||||
|
let serialized_ty = match bincode::serialize(&ty) {
|
||||||
|
Ok(bytes) => bytes,
|
||||||
|
Err(e) => return Err(RustError::SerdeError { error: MessageResource { key: "STORAGE.OBJECT_SERIALIZATION".into(), message: Some(e.to_string()) }, serde_error_str: None }),
|
||||||
|
};
|
||||||
|
match fs::write(format!("{}/{}", STORAGE_PATH.read().unwrap(), path), serialized_ty) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(RustError::IO { error: MessageResource { key:"STORAGE.OBJECT_WRITING".into(), message: Some(e.to_string())} }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn read<T: DeserializeOwned>(path: String) -> Result<T, RustError> {
|
||||||
|
let bytes = match fs::read(format!("{}/{}", STORAGE_PATH.read().unwrap(), path)) {
|
||||||
|
Ok(bytes) => bytes,
|
||||||
|
Err(e) => return Err(RustError::IO { error: MessageResource { key:"STORAGE.OBJECT_READING".into(), message: Some(e.to_string())} }),
|
||||||
|
};
|
||||||
|
match bincode::deserialize(&bytes) {
|
||||||
|
Ok(object) => Ok(object),
|
||||||
|
Err(e) => return Err(RustError::SerdeError { error: MessageResource { key: "STORAGE.OBJECT_DESERIALIZATION".into(), message: Some(e.to_string()) }, serde_error_str: None }),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user