/* * meli * * Copyright 2017-2018 Manos Pitsidianakis * * This file is part of meli. * * meli is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * meli is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ //! UI types used throughout meli. //! //! The [`segment_tree`] module performs maximum range queries. //! This is used in getting the maximum element of a column within a specific //! range in e-mail lists. //! That way a very large value that is not the in the currently displayed page //! does not cause the column to be rendered bigger than it has to. //! //! [`UIMode`] describes the application's... mode. Same as in the modal editor //! `vi`. //! //! [`UIEvent`] is the type passed around //! [`Component`]'s when something happens. #[macro_use] mod helpers; use std::{borrow::Cow, sync::Arc}; pub use helpers::*; use melib::{ backends::{AccountHash, BackendEvent, MailboxHash}, error::Error, EnvelopeHash, RefreshEvent, ThreadHash, }; use nix::unistd::Pid; use super::{ command::Action, jobs::{JobExecutor, JobId, TimerId}, terminal::*, }; use crate::components::{Component, ComponentId, ScrollUpdate}; pub type UIMessage = Box; #[derive(Debug)] pub enum StatusEvent { DisplayMessage(String), BufClear, BufSet(String), UpdateStatus(String), UpdateSubStatus(String), NewJob(JobId), JobFinished(JobId), JobCanceled(JobId), SetMouse(bool), ScrollUpdate(ScrollUpdate), } /// [`ThreadEvent`] encapsulates all of the possible values we need to transfer /// between our threads to the main process. #[derive(Debug)] pub enum ThreadEvent { /// User input and input as raw bytes. Input((Key, Vec)), /// A watched Mailbox has been refreshed. RefreshMailbox(Box), UIEvent(UIEvent), /// A thread has updated some of its information Pulse, JobFinished(JobId), } impl From for ThreadEvent { fn from(event: RefreshEvent) -> Self { Self::RefreshMailbox(Box::new(event)) } } impl From for ThreadEvent { fn from(event: UIEvent) -> Self { Self::UIEvent(event) } } #[derive(Debug)] pub enum ForkType { /// Already finished fork, we only want to restore input/output Finished, /// Embedded pty Embedded { id: Cow<'static, str>, command: Option>, pid: Pid, }, Generic { id: Cow<'static, str>, command: Option>, child: std::process::Child, }, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum NotificationType { Info, Error(melib::error::ErrorKind), NewMail, SentMail, Saved, } impl std::fmt::Display for NotificationType { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { Self::Info => write!(f, "info"), Self::Error(melib::error::ErrorKind::None) => write!(f, "error"), Self::Error(kind) => write!(f, "error: {}", kind), Self::NewMail => write!(f, "new mail"), Self::SentMail => write!(f, "sent mail"), Self::Saved => write!(f, "saved"), } } } #[derive(Debug)] pub enum UIEvent { Input(Key), CmdInput(Key), InsertInput(Key), EmbeddedInput((Key, Vec)), Resize, Fork(ForkType), ChangeMailbox(usize), ChangeMode(UIMode), Command(String), Notification { title: Option>, body: Cow<'static, str>, source: Option, kind: Option, }, Action(Action), StatusEvent(StatusEvent), MailboxUpdate((AccountHash, MailboxHash)), // (account_idx, mailbox_idx) MailboxDelete((AccountHash, MailboxHash)), MailboxCreate((AccountHash, MailboxHash)), AccountStatusChange(AccountHash, Option>), ComponentUnrealize(ComponentId), BackendEvent(AccountHash, BackendEvent), StartupCheck(MailboxHash), RefreshEvent(Box), EnvelopeUpdate(EnvelopeHash), EnvelopeRename(EnvelopeHash, EnvelopeHash), // old_hash, new_hash EnvelopeRemove(EnvelopeHash, ThreadHash), Contacts(ContactEvent), Compose(ComposeEvent), FinishedUIDialog(ComponentId, UIMessage), IntraComm { from: ComponentId, to: ComponentId, content: UIMessage, }, Callback(CallbackFn), GlobalUIDialog { value: Box, parent: Option, }, Timer(TimerId), ConfigReload { old_settings: Box, }, VisibilityChange(bool), } pub struct CallbackFn(pub Box); impl std::fmt::Debug for CallbackFn { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { write!(fmt, "CallbackFn") } } impl From for UIEvent { fn from(event: RefreshEvent) -> Self { Self::RefreshEvent(Box::new(event)) } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum UIMode { Normal, Insert, /// Forward input to an embedded pseudoterminal. Embedded, Command, Fork, } impl std::fmt::Display for UIMode { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "{}", match *self { Self::Normal => "NORMAL", Self::Insert => "INSERT", Self::Command => "COMMAND", Self::Fork => "FORK", Self::Embedded => "EMBEDDED", } ) } } pub mod segment_tree { //! Simple segment tree implementation for maximum in range queries. This is //! useful if given an array of numbers you want to get the maximum //! value inside an interval quickly. use std::{convert::TryFrom, iter::FromIterator}; use smallvec::SmallVec; #[derive(Clone, Debug, Default)] pub struct SegmentTree { pub array: SmallVec<[u8; 1024]>, tree: SmallVec<[u8; 1024]>, } impl From> for SegmentTree { fn from(val: SmallVec<[u8; 1024]>) -> Self { Self::new(val) } } impl SegmentTree { pub fn new(val: SmallVec<[u8; 1024]>) -> Self { if val.is_empty() { return Self { array: val.clone(), tree: val, }; } let height = (f64::from(u32::try_from(val.len()).unwrap_or(0))) .log2() .ceil() as u32; let max_size = 2 * (2_usize.pow(height)); let mut segment_tree: SmallVec<[u8; 1024]> = SmallVec::from_iter(std::iter::repeat(0).take(max_size)); for i in 0..val.len() { segment_tree[val.len() + i] = val[i]; } for i in (1..val.len()).rev() { segment_tree[i] = std::cmp::max(segment_tree[2 * i], segment_tree[2 * i + 1]); } Self { array: val, tree: segment_tree, } } /// (left, right) is inclusive pub fn get_max(&self, mut left: usize, mut right: usize) -> u8 { if self.array.is_empty() { return 0; } let len = self.array.len(); debug_assert!(left <= right); if right >= len { right = len.saturating_sub(1); } left += len; right += len + 1; let mut max = 0; while left < right { if (left & 1) > 0 { max = std::cmp::max(max, self.tree[left]); left += 1; } if (right & 1) > 0 { right -= 1; max = std::cmp::max(max, self.tree[right]); } left /= 2; right /= 2; } max } pub fn update(&mut self, pos: usize, value: u8) { let mut ctr = pos + self.array.len(); // Update leaf node value self.tree[ctr] = value; while ctr > 1 { // move up one level ctr >>= 1; self.tree[ctr] = std::cmp::max(self.tree[2 * ctr], self.tree[2 * ctr + 1]); } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_segment_tree() { let array: SmallVec<[u8; 1024]> = [9, 1, 17, 2, 3, 23, 4, 5, 6, 37] .iter() .cloned() .collect::>(); let mut segment_tree = SegmentTree::from(array); assert_eq!(segment_tree.get_max(0, 5), 23); assert_eq!(segment_tree.get_max(6, 9), 37); segment_tree.update(2_usize, 24_u8); assert_eq!(segment_tree.get_max(0, 5), 24); } } } #[derive(Debug)] pub struct RateLimit { last_tick: std::time::Instant, pub timer: crate::jobs::Timer, rate: std::time::Duration, pub active: bool, } impl RateLimit { pub fn new(reqs: u64, millis: u64, job_executor: Arc) -> Self { Self { last_tick: std::time::Instant::now(), timer: job_executor.create_timer( std::time::Duration::from_secs(0), std::time::Duration::from_millis(millis), ), rate: std::time::Duration::from_millis(millis / reqs), active: false, } } pub fn reset(&mut self) { self.last_tick = std::time::Instant::now(); self.active = false; } pub fn tick(&mut self) -> bool { let now = std::time::Instant::now(); if self.last_tick + self.rate > now { self.active = false; } else { self.timer.rearm(); self.last_tick = now; self.active = true; } self.active } #[inline(always)] pub fn id(&self) -> TimerId { self.timer.id() } } #[derive(Debug)] pub enum ContactEvent { CreateContacts(Vec), } #[derive(Debug)] pub enum ComposeEvent { SetRecipients(Vec), } #[cfg(test)] mod tests { //use super::*; #[test] fn test_rate_limit() { /* let (sender, receiver) = crossbeam::channel::bounded(4096 * ::std::mem::size_of::()); use std::sync::Arc; let job_executor = Arc::new(JobExecutor::new(sender)); /* Accept at most one request per 3 milliseconds */ let mut rt = RateLimit::new(1, 3, job_executor.clone()); std::thread::sleep(std::time::Duration::from_millis(2000)); /* assert that only one request per 3 milliseconds is accepted */ for _ in 0..5 { assert!(rt.tick()); std::thread::sleep(std::time::Duration::from_millis(1)); assert!(!rt.tick()); std::thread::sleep(std::time::Duration::from_millis(1)); assert!(!rt.tick()); std::thread::sleep(std::time::Duration::from_millis(1)); /* How many times was the signal handler called? We've slept for at least 3 * milliseconds, so it should have been called once */ let mut ctr = 0; while receiver.try_recv().is_ok() { ctr += 1; println!("got {}", ctr); } println!("ctr = {} {}", ctr, ctr == 1); assert_eq!(ctr, 1); } /* next, test at most 100 requests per second */ let mut rt = RateLimit::new(100, 1000, job_executor.clone()); for _ in 0..5 { let mut ctr = 0; for _ in 0..500 { if rt.tick() { ctr += 1; } std::thread::sleep(std::time::Duration::from_millis(2)); } /* around 100 requests should succeed. might be 99 if in first loop, since * RateLimit::new() has a delay */ assert!(ctr > 97 && ctr < 103); /* alarm should expire in 1 second */ std::thread::sleep(std::time::Duration::from_millis(1000)); /* How many times was the signal handler called? */ ctr = 0; while receiver.try_recv().is_ok() { ctr += 1; } assert_eq!(ctr, 1); } /* next, test at most 500 requests per second */ let mut rt = RateLimit::new(500, 1000, job_executor.clone()); for _ in 0..5 { let mut ctr = 0; for _ in 0..500 { if rt.tick() { ctr += 1; } std::thread::sleep(std::time::Duration::from_millis(2)); } /* all requests should succeed. */ assert!(ctr < 503 && ctr > 497); /* alarm should expire in 1 second */ std::thread::sleep(std::time::Duration::from_millis(1000)); /* How many times was the signal handler called? */ ctr = 0; while receiver.try_recv().is_ok() { ctr += 1; } assert_eq!(ctr, 1); } */ } }