Compare commits

...

4 Commits

Author SHA1 Message Date
Hocuri
db5cb45b9c Rustfmt, Comment, move a little bit slower 2020-10-12 16:05:18 +02:00
Hocuri
36046f5f2c Repair some tests 2020-10-12 15:33:54 +02:00
Hocuri
1f9c0ef7d9 Remove debug logs 2020-10-12 15:33:53 +02:00
Hocuri
efd62a7c04 Completely smooth progress bar 2020-10-12 15:33:49 +02:00
3 changed files with 150 additions and 66 deletions

View File

@@ -2084,7 +2084,6 @@ class TestOnlineConfigureFails:
ac1, configdict = acfactory.get_online_config()
ac1.update_config(dict(addr=configdict["addr"], mail_pw="123"))
configtracker = ac1.configure()
configtracker.wait_progress(500)
configtracker.wait_progress(0)
ac1._evtracker.ensure_event_not_queued("DC_EVENT_ERROR_NETWORK")
@@ -2092,7 +2091,6 @@ class TestOnlineConfigureFails:
ac1, configdict = acfactory.get_online_config()
ac1.update_config(dict(addr="x" + configdict["addr"], mail_pw=configdict["mail_pw"]))
configtracker = ac1.configure()
configtracker.wait_progress(500)
configtracker.wait_progress(0)
ac1._evtracker.ensure_event_not_queued("DC_EVENT_ERROR_NETWORK")
@@ -2100,6 +2098,5 @@ class TestOnlineConfigureFails:
ac1, configdict = acfactory.get_online_config()
ac1.update_config((dict(addr=configdict["addr"] + "x", mail_pw=configdict["mail_pw"])))
configtracker = ac1.configure()
configtracker.wait_progress(500)
configtracker.wait_progress(0)
ac1._evtracker.ensure_event_not_queued("DC_EVENT_ERROR_NETWORK")

View File

@@ -5,46 +5,31 @@ mod auto_outlook;
mod read_url;
mod server_params;
use anyhow::{bail, ensure, Context as _, Result};
use async_std::prelude::*;
use async_std::task;
use itertools::Itertools;
use job::Action;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use crate::config::Config;
use crate::constants::*;
use crate::context::Context;
use crate::dc_tools::*;
use crate::imap::Imap;
use crate::job;
use crate::login_param::{LoginParam, ServerLoginParam};
use crate::message::Message;
use crate::oauth2::*;
use crate::param::Params;
use crate::provider::{Protocol, Socket, UsernamePattern};
use crate::smtp::Smtp;
use crate::stock::StockMessage;
use crate::EventType;
use crate::{chat, e2ee, provider};
use crate::{constants::*, job};
use crate::{context::Context, param::Params};
use anyhow::{bail, ensure, Context as _, Result};
use async_std::prelude::*;
use async_std::task;
use auto_mozilla::moz_autoconfigure;
use auto_outlook::outlk_autodiscover;
use itertools::Itertools;
use job::Action;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use server_params::{expand_param_vector, ServerParams};
macro_rules! progress {
($context:tt, $progress:expr, $comment:expr) => {
assert!(
$progress <= 1000,
"value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success"
);
$context.emit_event($crate::events::EventType::ConfigureProgress {
progress: $progress,
comment: $comment,
});
};
($context:tt, $progress:expr) => {
progress!($context, $progress, None);
};
}
impl Context {
/// Checks if the context is already configured.
pub async fn is_configured(&self) -> bool {
@@ -65,10 +50,18 @@ impl Context {
);
let cancel_channel = self.alloc_ongoing().await?;
let ctx2 = self.clone();
let progress = ProgressHandler::new(15.0, move |p| {
ctx2.emit_event(EventType::ConfigureProgress {
progress: p,
comment: None,
});
});
let res = self
.inner_configure()
.inner_configure(&progress)
.race(cancel_channel.recv().map(|_| {
progress!(self, 0);
progress.p(0);
Ok(())
}))
.await;
@@ -78,11 +71,11 @@ impl Context {
res
}
async fn inner_configure(&self) -> Result<()> {
async fn inner_configure(&self, progress: &impl Progress) -> Result<()> {
info!(self, "Configure ...");
let mut param = LoginParam::from_database(self, "").await;
let success = configure(self, &mut param).await;
let success = configure(self, &mut param, progress).await;
self.set_config(Config::NotifyAboutWrongPw, None).await?;
if let Some(provider) = provider::get_provider_info(&param.addr) {
@@ -116,21 +109,24 @@ impl Context {
Ok(_) => {
self.set_config(Config::NotifyAboutWrongPw, Some("1"))
.await?;
progress!(self, 1000);
progress.p(1000);
Ok(())
}
Err(err) => {
progress!(
progress.kill().await;
emit_event!(
self,
0,
Some(
self.stock_string_repl_str(
StockMessage::ConfigurationFailed,
// We are using Anyhow's .context() and to show the inner error, too, we need the {:#}:
format!("{:#}", err),
EventType::ConfigureProgress {
progress: 0,
comment: Some(
self.stock_string_repl_str(
StockMessage::ConfigurationFailed,
// We are using Anyhow's .context() and to show the inner error too, we need the {:#}:
format!("{:#}", err),
)
.await
)
.await
)
}
);
Err(err)
}
@@ -138,8 +134,8 @@ impl Context {
}
}
async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
progress!(ctx, 1);
async fn configure(ctx: &Context, param: &mut LoginParam, progress: &impl Progress) -> Result<()> {
progress.p(1);
// Check basic settings.
ensure!(!param.addr.is_empty(), "Please enter an email address.");
@@ -168,7 +164,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
if oauth2 {
// the used oauth2 addr may differ, check this.
// if dc_get_oauth2_addr() is not available in the oauth2 implementation, just use the given one.
progress!(ctx, 10);
progress.p(10);
if let Some(oauth2_addr) = dc_get_oauth2_addr(ctx, &param.addr, &param.imap.password)
.await
.and_then(|e| e.parse().ok())
@@ -179,7 +175,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
.set_raw_config(ctx, "addr", Some(param.addr.as_str()))
.await?;
}
progress!(ctx, 20);
progress.p(20);
}
// no oauth? - just continue it's no error
@@ -188,7 +184,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
let param_addr_urlencoded = utf8_percent_encode(&param.addr, NON_ALPHANUMERIC).to_string();
// Step 2: Autoconfig
progress!(ctx, 200);
progress.p(200);
let param_autoconfig;
if param.imap.server.is_empty()
@@ -206,13 +202,13 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
param_autoconfig = Some(servers);
} else {
param_autoconfig =
get_autoconfig(ctx, param, &param_domain, &param_addr_urlencoded).await;
get_autoconfig(ctx, param, &param_domain, &param_addr_urlencoded, progress).await;
}
} else {
param_autoconfig = None;
}
progress!(ctx, 500);
progress.p(500);
let servers = expand_param_vector(
param_autoconfig.unwrap_or_else(|| {
@@ -237,7 +233,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
&param_domain,
);
progress!(ctx, 550);
progress.p(550);
// Spawn SMTP configuration task
let mut smtp = Smtp::new();
@@ -278,7 +274,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
}
});
progress!(ctx, 600);
progress.p(600);
// Configure IMAP
let (_s, r) = async_std::sync::channel(1);
@@ -304,16 +300,13 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
}
Err(e) => errors.push(e),
}
progress!(
ctx,
600 + (800 - 600) * (1 + imap_server_index) / imap_servers_count
);
progress.p(600 + (800 - 600) * (1 + imap_server_index) / imap_servers_count);
}
if !imap_configured {
bail!(nicer_configuration_error(ctx, errors).await);
}
progress!(ctx, 850);
progress.p(850);
// Wait for SMTP configuration
match smtp_config_task.await {
@@ -325,7 +318,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
}
}
progress!(ctx, 900);
progress.p(900);
let create_mvbox = ctx.get_config_bool(Config::MvboxWatch).await
|| ctx.get_config_bool(Config::MvboxMove).await;
@@ -338,14 +331,14 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
drop(imap);
progress!(ctx, 910);
progress.p(910);
// configuration success - write back the configured parameters with the
// "configured_" prefix; also write the "configured"-flag */
// the trailing underscore is correct
param.save_to_database(ctx, "configured_").await?;
ctx.sql.set_raw_config_bool(ctx, "configured", true).await?;
progress!(ctx, 920);
progress.p(920);
e2ee::ensure_secret_key_exists(ctx).await?;
info!(ctx, "key generation completed");
@@ -356,7 +349,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
)
.await;
progress!(ctx, 940);
progress.p(940);
Ok(())
}
@@ -430,14 +423,15 @@ async fn get_autoconfig(
param: &LoginParam,
param_domain: &str,
param_addr_urlencoded: &str,
progress: &impl Progress,
) -> Option<Vec<ServerParams>> {
let sources = AutoconfigSource::all(param_domain, param_addr_urlencoded);
let mut progress = 300;
let mut p = 300;
for source in &sources {
let res = source.fetch(ctx, param).await;
progress!(ctx, progress);
progress += 10;
progress.p(p);
p += 10;
if let Ok(res) = res {
return Some(res);
}

View File

@@ -2,15 +2,20 @@
//! no references to Context and other "larger" entities here.
use core::cmp::{max, min};
use std::borrow::Cow;
use std::fmt;
use std::io::Cursor;
use std::str::FromStr;
use std::time::{Duration, SystemTime};
use std::{borrow::Cow, sync::Arc};
use async_std::path::{Path, PathBuf};
use async_std::prelude::*;
use async_std::{fs, io};
use async_std::{
path::{Path, PathBuf},
sync::Mutex,
task,
};
use async_trait::async_trait;
use chrono::{Local, TimeZone};
use rand::{thread_rng, Rng};
@@ -24,6 +29,94 @@ use crate::message::Message;
use crate::provider::get_provider_update_timestamp;
use crate::stock::StockMessage;
#[derive(Debug)]
pub struct ProgressHandlerInner<F: Fn(usize) + Send> {
progress_limit: usize,
emitted_progress: f64,
step_fraction: f64,
f: F,
}
#[derive(Debug)]
pub struct ProgressHandler<F: 'static + Fn(usize) + Send> {
inner: Arc<Mutex<ProgressHandlerInner<F>>>,
}
impl<F> ProgressHandler<F>
where
F: 'static + Fn(usize) + Send,
{
/// If step_fraction is e.g. 15, then every 100ms we will step by 1/15th of the remaining interval.
/// The bigger this value, the slower the progress bar will move in the beginning.
/// f is the function that is invoked when progress is made.
pub fn new(step_fraction: f64, f: F) -> Self {
let ret = Arc::new(Mutex::new(ProgressHandlerInner {
progress_limit: 1,
emitted_progress: 0f64,
step_fraction,
f,
}));
let cloned = ret.clone();
task::spawn(async move {
loop {
task::sleep(Duration::from_millis(100)).await;
{
let mut lock = cloned.lock().await;
let limit = lock.progress_limit;
if limit == 1000 || limit == 0 {
return;
}
let last = lock.emitted_progress;
let next = last + ((limit as f64 - last) / lock.step_fraction);
if (next / 10f64).ceil() - (last / 10f64).ceil() > 0f64 {
(lock.f)(next.ceil() as usize);
}
lock.emitted_progress = next;
drop(lock);
};
}
});
Self { inner: ret }
}
}
#[async_trait]
pub trait Progress {
/// Report actually made progress 0-1000 promille. The progress bar will slowly move toward the value set by this function.
/// Set rather high values as the progress bar will stay lower first,
/// i.e. don't start with values near 0 and end with values near 1000
fn p(&self, progress: usize);
/// Stops the progress handler without emitting any other events
async fn kill(&self);
}
#[async_trait]
impl<F> Progress for ProgressHandler<F>
where
F: 'static + Fn(usize) + Send,
{
fn p(&self, progress: usize) {
assert!(
progress <= 1000,
"value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success"
);
let inner = self.inner.clone();
task::spawn(async move {
if progress == 1000 || progress == 0 {
let inner = &inner.lock().await;
(inner.f)(progress);
}
inner.lock().await.progress_limit = progress;
});
}
async fn kill(&self) {
self.inner.lock().await.progress_limit = 0usize;
}
}
/// Shortens a string to a specified length and adds "[...]" to the
/// end of the shortened string.
#[allow(clippy::indexing_slicing)]