Browse Source

Add handling loop iteration for python

master
Lev 3 years ago
parent
commit
05785e078c
  1. 34
      degeon/src/state.rs
  2. 4
      degeon_core/src/chat.rs
  3. 109
      degeon_core/src/degeon_worker.rs
  4. 6
      degeon_core/src/lib.rs
  5. 69
      degeon_core/src/message.rs
  6. 2
      src/ironforce.rs

34
degeon/src/state.rs

@ -198,14 +198,6 @@ impl Application for DegeonApp {
name_input_state: Default::default(),
profile_done_button_state: Default::default(),
};
let data_clone = st.data.clone();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_secs(10));
loop {
data_clone.send_multicast(ProtocolMsg::Ping).unwrap();
std::thread::sleep(std::time::Duration::from_secs(120));
}
});
(st, iced::Command::none())
}
@ -214,6 +206,7 @@ impl Application for DegeonApp {
}
fn update(&mut self, message: GuiEvent, _: &mut iced::Clipboard) -> iced::Command<GuiEvent> {
self.data.process_event(&message, false).unwrap();
match message {
GuiEvent::ChatSelect(i) => self.selected_chat = i,
GuiEvent::Typed(st) => self.data.chats[self.selected_chat].input = st,
@ -238,22 +231,14 @@ impl Application for DegeonApp {
});
self.data.save_to_file("".to_string()).unwrap();
}
GuiEvent::NewMessageInChat(pkey, msg) => {
if self.data.chat_with(&pkey).is_none() {
self.data.chats.push(Chat::new(pkey.clone()))
}
let ind = self.data.chat_with(&pkey).unwrap();
self.data.chats[ind].messages.push(msg);
self.data.save_to_file("".to_string()).unwrap();
}
GuiEvent::SetProfile(pkey, name) => {
if self.data.chat_with(&pkey).is_none() {
self.data.chats.push(Chat::new(pkey.clone()))
}
let ind = self.data.chat_with(&pkey).unwrap();
self.data.chats[ind].profile = name;
GuiEvent::ChangeScreen(sc) => {
self.screen = sc;
self.data.save_to_file("".to_string()).unwrap();
}
GuiEvent::ChangeName(name) => self.data.profile.name = name,
// The following events are already handled in Degeon::process_event
GuiEvent::NewMessageInChat(_pkey, _msg) => {}
GuiEvent::SetProfile(_pkey, _profile) => { }
GuiEvent::None => {}
GuiEvent::WeHaveToSendProfile(target) => {
println!("WHTSP");
@ -262,11 +247,6 @@ impl Application for DegeonApp {
&target,
);
}
GuiEvent::ChangeScreen(sc) => {
self.screen = sc;
self.data.save_to_file("".to_string()).unwrap();
}
GuiEvent::ChangeName(name) => self.data.profile.name = name,
}
iced::Command::none()
}

4
degeon_core/src/chat.rs

@ -1,16 +1,20 @@
use ironforce::{Keys, PublicKey};
use crate::message::{DegMessage, DegMessageContent, Profile};
use serde::{Serialize, Deserialize};
use pyo3::prelude::*;
/// A chat in the messenger
#[derive(Clone, Debug, Serialize, Deserialize)]
#[pyclass]
pub struct Chat {
/// Public key of the other user
pub pkey: PublicKey,
/// Messages in this chat
#[pyo3(get)]
pub messages: Vec<DegMessage>,
/// Profile of the other user
#[pyo3(get, set)]
pub profile: Profile,
/// Scroll position
pub scrolled: f32,

109
degeon_core/src/degeon_worker.rs

@ -1,22 +1,25 @@
use crate::chat::Chat;
use crate::gui_events::GuiEvent;
use crate::message::{Profile, ProtocolMsg};
use crate::DegMessage;
use futures::Stream;
use ironforce::res::IFResult;
use ironforce::{IronForce, Keys, Message, MessageType, PublicKey};
use pyo3::prelude::*;
use serde::{Deserialize, Serialize};
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use pyo3::prelude::*;
use serde::{Serialize, Deserialize};
/// The container for logic, data, IF and protocol interactions
#[derive(Clone)]
#[pyclass]
pub struct Degeon {
/// The list of all chats for this instance
#[pyo3(get, set)]
pub chats: Vec<Chat>,
/// Profile of this user
#[pyo3(get, set)]
pub profile: Profile,
/// Keys of this user
pub keys: Keys,
@ -46,12 +49,21 @@ fn get_initialized_ironforce() -> (Arc<Mutex<IronForce>>, Keys) {
impl Default for Degeon {
fn default() -> Self {
let (ironforce, keys) = get_initialized_ironforce();
Self {
let st = Self {
chats: vec![],
profile: Profile::default(),
keys,
ironforce,
};
let self_clone = st.clone();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_secs(2));
loop {
self_clone.send_multicast(ProtocolMsg::Ping).unwrap();
std::thread::sleep(std::time::Duration::from_secs(120));
}
});
st
}
}
@ -122,6 +134,7 @@ impl Degeon {
// }
// }
// }
println!("Sending: {:?}", msg);
self.ironforce.lock().unwrap().send_to_all(
Message::build()
.message_type(MessageType::Broadcast)
@ -162,6 +175,66 @@ impl Degeon {
GuiEvent::None
})
}
/// Process a GuiEvent if it's connected to the worker (like sending a message)
pub fn process_event(&mut self, event: &GuiEvent, perform_sending: bool) -> IFResult<()> {
match event {
GuiEvent::NewMessageInChat(pkey, msg) => {
if self.chat_with(&pkey).is_none() {
self.chats.push(Chat::new(pkey.clone()))
}
let ind = self.chat_with(&pkey).unwrap();
self.chats[ind].messages.push(msg.clone());
self.save_to_file("".to_string())?;
}
GuiEvent::SetProfile(pkey, profile) => {
if self.chat_with(&pkey).is_none() {
self.chats.push(Chat::new(pkey.clone()))
}
let ind = self.chat_with(&pkey).unwrap();
self.chats[ind].profile = profile.clone();
self.save_to_file("".to_string())?;
}
GuiEvent::WeHaveToSendProfile(target) if perform_sending => {
let target = target.clone();
let self_cloned = self.clone();
std::thread::spawn(move || {
self_cloned.send_message(
ProtocolMsg::ProfileResponse(self_cloned.get_profile()),
&target,
)
});
}
_ => {}
}
Ok(())
}
}
#[pymethods]
impl Degeon {
/// Create a new text message and send it
pub fn send_text_message(&self, text: String, chat_i: usize) -> PyResult<()> {
self.send_message(
ProtocolMsg::NewMessage(DegMessage::new_text(text, &self.keys.get_public())),
&self.chats[chat_i].pkey,
)
.map_err(|e| {
pyo3::exceptions::PyTypeError::new_err(format!("There was an error in Rust: {:?}", e))
})
}
/// Handle one message
pub fn handling_loop_iteration(&mut self) {
let event = self.read_message_and_create_event();
if let Some(event) = event {
self.process_event(&event, true).unwrap_or_else(|e| println!("Error: {:?}", e));
}
}
pub fn message_queue_len(&self) -> usize {
self.ironforce.lock().unwrap().messages.len()
}
}
pub const DEFAULT_FILENAME: &str = ".degeon.json";
@ -190,7 +263,7 @@ impl Degeon {
chats: data.chats,
profile: data.profile,
keys: data.keys,
ironforce
ironforce,
};
Ok(deg)
}
@ -219,11 +292,8 @@ impl Degeon {
}
}
impl Stream for Degeon {
type Item = GuiEvent;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let timestamp_0 = std::time::Instant::now();
impl Degeon {
pub fn read_message_and_create_event(&self) -> Option<GuiEvent> {
let msg_raw = self.ironforce.lock().unwrap().read_message();
let msg = msg_raw
.as_ref()
@ -240,18 +310,27 @@ impl Stream for Degeon {
Ok(r) => r,
Err(_) => {
println!("Couldn't deserialize {:?}", msg_raw);
return Poll::Ready(Some(GuiEvent::None));
return Some(GuiEvent::None);
}
};
println!("{:?} -> {:?}", msg_deg, msg);
}
match msg {
Some(Some(event)) => Some(event),
_ => None
}
}
}
impl Stream for Degeon {
type Item = GuiEvent;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let timestamp_0 = std::time::Instant::now();
let msg = self.read_message_and_create_event();
if timestamp_0.elapsed() < std::time::Duration::from_millis(5) {
std::thread::sleep(std::time::Duration::from_millis(5));
}
match msg {
None => Poll::Ready(Some(GuiEvent::None)),
Some(None) => Poll::Ready(Some(GuiEvent::None)),
Some(Some(msg)) => Poll::Ready(Some(msg)),
}
Poll::Ready(Some(msg.unwrap_or(GuiEvent::None)))
}
}

6
degeon_core/src/lib.rs

@ -7,19 +7,21 @@ mod message;
pub use chat::Chat;
pub use degeon_worker::{Degeon, DegeonData, DEFAULT_FILENAME};
pub use message::{DegMessage, Profile, ProtocolMsg, DegMessageContent};
pub use message::{DegMessage, Profile, ProtocolMsg, DegMessageContent, DegMessageContentPy};
pub use gui_events::{AppScreen, GuiEvent};
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
#[pyfunction]
fn new_degeon() -> PyResult<Degeon> {
Ok(Degeon::default())
Ok(Degeon::restore_from_file("".to_string()).unwrap())
}
#[pymodule]
fn degeon_core(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<Degeon>()?;
m.add_class::<DegMessageContentPy>()?;
m.add_class::<DegMessage>()?;
m.add_function(wrap_pyfunction!(new_degeon, m)?)?;
Ok(())
}

69
degeon_core/src/message.rs

@ -1,8 +1,10 @@
use ironforce::PublicKey;
use pyo3::prelude::*;
use serde::{Deserialize, Serialize};
/// A message in the messenger
#[derive(Clone, Debug, Serialize, Deserialize)]
#[pyclass]
pub struct DegMessage {
pub sender: PublicKey,
pub timestamp: i64,
@ -20,6 +22,13 @@ impl DegMessage {
}
}
#[pymethods]
impl DegMessage {
pub fn get_content_py(&self) -> DegMessageContentPy {
self.content.get_py()
}
}
/// The content of the message
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum DegMessageContent {
@ -30,7 +39,9 @@ pub enum DegMessageContent {
/// User's profile
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
#[pyclass]
pub struct Profile {
#[pyo3(get, set)]
pub name: String,
}
@ -46,3 +57,61 @@ pub enum ProtocolMsg {
/// A message is sent
NewMessage(DegMessage),
}
/// DegMessageContent, but a struct for python
#[derive(Clone, Debug, Serialize, Deserialize)]
#[pyclass]
pub struct DegMessageContentPy {
/// If `DegMessageContent` is `Text(st)`, then this is `Some(st)`
#[pyo3(get, set)]
pub text: Option<String>,
/// If `DegMessageContent` is `File(data)`, then this is `Some(data)`
#[pyo3(get, set)]
pub file: Option<Vec<u8>>,
}
impl DegMessageContent {
/// Convert from `DegMessageContent` to the corresponding `DegMessageContentPy`
pub fn get_py(&self) -> DegMessageContentPy {
match self {
DegMessageContent::Text(text) => DegMessageContentPy {
text: Some(text.clone()),
file: None,
},
DegMessageContent::File(data) => DegMessageContentPy {
text: None,
file: Some(data.clone()),
},
DegMessageContent::Service => DegMessageContentPy {
text: None,
file: None,
},
}
}
}
impl DegMessageContentPy {
/// Convert from `DegMessageContentPy` to the corresponding `DegMessageContent`
pub fn to_enum(&self) -> DegMessageContent {
match self {
DegMessageContentPy {
text: Some(text), ..
} => DegMessageContent::Text(text.clone()),
DegMessageContentPy {
file: Some(data), ..
} => DegMessageContent::File(data.clone()),
_ => DegMessageContent::Service,
}
}
}
#[pymethods]
impl DegMessageContentPy {
#[staticmethod]
pub fn new_text(text: String) -> Self {
Self {
text: Some(text),
file: None,
}
}
}

2
src/ironforce.rs

@ -30,7 +30,7 @@ pub struct IronForce {
/// and some kind of decentralized storage
additional_modules: Vec<()>,
/// Non-service messages to give outside
messages: Vec<Message>,
pub messages: Vec<Message>,
/// Tunnels that has not been confirmed yet (no backward spread)
///
/// `[(Tunnel, Optional target node, local peer ids)]`

Loading…
Cancel
Save