Skip to content

Commit

Permalink
feat: loading spinner (#93)
Browse files Browse the repository at this point in the history
Signed-off-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
berendsliedrecht authored Mar 9, 2022
1 parent 7c62338 commit cebbd1c
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 32 deletions.
4 changes: 2 additions & 2 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ extern crate log;

mod cli;
mod error;
mod help_strings;
mod modules;
mod register;
mod utils;
mod help_strings;

use colored::*;
use log::error;
use register::register;
use colored::*;

#[tokio::main]
async fn main() {
Expand Down
17 changes: 11 additions & 6 deletions cli/src/modules/connections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use log::info;

use crate::error::{Error, Result};
use crate::utils::{
loader::{Loader, LoaderVariant},
logger::{copy_to_clipboard, pretty_print_obj},
qr::print_qr_code,
};
Expand Down Expand Up @@ -37,23 +38,26 @@ pub enum ConnectionSubcommands {
},
}

// TODO: we should implement `from` so we can use todo and have a cleaner api
pub async fn parse_connection_args(
options: &ConnectionOptions,
agent: impl ConnectionModule,
copy: bool,
) -> Result<()> {
let loader = Loader::start(LoaderVariant::default());
if let Some(id) = &options.id {
return agent
.get_connection_by_id(id.to_string())
.await
.map(|connection| pretty_print_obj(connection));
.map(|connections| {
loader.stop();
pretty_print_obj(connections)
});
}
if options.all {
return agent
.get_connections()
.await
.map(|connections| pretty_print_obj(connections.results));
return agent.get_connections().await.map(|connections| {
loader.stop();
pretty_print_obj(connections.results)
});
}
match &options
.commands
Expand All @@ -75,6 +79,7 @@ pub async fn parse_connection_args(
toolbox: *toolbox,
};
agent.create_invitation(options).await.map(|response| {
loader.stop();
if *qr {
info!("{}", format!("{}: {}", "Connection id".green(), response.0));
print_qr_code(response.1).unwrap();
Expand Down
18 changes: 12 additions & 6 deletions cli/src/modules/credential_definition.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use agent_controller::modules::credential_definition::CredentialDefinitionModule;
use clap::{Args, Subcommand};
use serde_json::json;
use log::{debug, info};
use serde_json::json;

use crate::{
error::{Error, Result},
utils::logger::{pretty_stringify_obj},
utils::loader::{Loader, LoaderVariant},
utils::logger::pretty_stringify_obj,
};

#[derive(Args)]
Expand All @@ -32,8 +33,10 @@ pub async fn parse_credential_definition_args(
options: &CredentialDefinitionOptions,
agent: impl CredentialDefinitionModule,
) -> Result<()> {
let loader = Loader::start(LoaderVariant::default());
if let Some(id) = &options.id {
return agent.get_by_id(id.to_string()).await.map(|cred_def| {
loader.stop();
let loggable = json!({
"id": cred_def.credential_definition.id,
"schema_id": cred_def.credential_definition.schema_id,
Expand All @@ -46,6 +49,7 @@ pub async fn parse_credential_definition_args(
}
if options.all {
return agent.get_all().await.map(|cred_defs| {
loader.stop();
cred_defs
.credential_definition_ids
.iter()
Expand All @@ -57,9 +61,11 @@ pub async fn parse_credential_definition_args(
.as_ref()
.ok_or_else(|| Error::NoSubcommandSupplied("credential-definition".to_string()))?
{
CredentialDefinitionSubcommands::Create { schema_id } => agent
.create(schema_id.to_string())
.await
.map(|cred_def| info!("{}", cred_def.sent.credential_definition_id)),
CredentialDefinitionSubcommands::Create { schema_id } => {
agent.create(schema_id.to_string()).await.map(|cred_def| {
loader.stop();
info!("{}", cred_def.sent.credential_definition_id);
})
}
}
}
7 changes: 6 additions & 1 deletion cli/src/modules/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use clap::{Args, Subcommand};
use log::{debug, info};

use crate::error::{Error, Result};
use crate::utils::logger::pretty_stringify_obj;
use crate::utils::{
loader::{Loader, LoaderVariant},
logger::pretty_stringify_obj,
};
use colored::*;

#[derive(Args)]
Expand Down Expand Up @@ -34,6 +37,7 @@ pub async fn parse_credentials_args(
commands: &CredentialSubcommands,
agent: impl CredentialsModule,
) -> Result<()> {
let loader = Loader::start(LoaderVariant::default());
match commands {
CredentialSubcommands::Offer {
connection_id,
Expand All @@ -52,6 +56,7 @@ pub async fn parse_credentials_args(
values: value.iter().map(|v| v.to_string()).collect(),
};
agent.send_offer(options).await.map(|res| {
loader.stop();
debug!("{}", pretty_stringify_obj(res));
info!("{} offered a credential", "Sucessefully".green());
})
Expand Down
7 changes: 6 additions & 1 deletion cli/src/modules/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ use clap::Args;
use log::{debug, info};

use crate::error::Result;
use crate::utils::logger::pretty_stringify_obj;
use crate::utils::{
loader::{Loader, LoaderVariant},
logger::pretty_stringify_obj,
};

#[derive(Args)]
pub struct FeaturesOptions {}

pub async fn parse_features_args(agent: impl FeaturesModule) -> Result<()> {
let loader = Loader::start(LoaderVariant::default());
agent.discover_features().await.map(|features| {
loader.stop();
debug!("{}", pretty_stringify_obj(&features));
features
.results
Expand Down
10 changes: 6 additions & 4 deletions cli/src/modules/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use colored::*;
use log::info;

use crate::error::Result;
use crate::utils::loader::{Loader, LoaderVariant};

#[derive(Args)]
pub struct MessageOptions {
Expand All @@ -14,12 +15,13 @@ pub struct MessageOptions {
}

pub async fn parse_message_args(options: &MessageOptions, agent: impl MessageModule) -> Result<()> {
let loader = Loader::start(LoaderVariant::default());
let send_options = SendMessageOptions {
id: options.id.to_owned(),
message: options.message.to_owned(),
};
agent
.send_message(send_options)
.await
.map(|msg| info!("{} sent message: {}", "Successfully".green(), msg))
agent.send_message(send_options).await.map(|msg| {
loader.stop();
info!("{} sent message: {}", "Successfully".green(), msg)
})
}
26 changes: 14 additions & 12 deletions cli/src/modules/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use log::info;

use crate::{
error::{Error, Result},
utils::loader::{Loader, LoaderVariant},
utils::logger::pretty_print_obj,
};

Expand Down Expand Up @@ -32,17 +33,18 @@ pub enum SchemaSubcommands {
}

pub async fn parse_schema_args(options: &SchemaOptions, agent: impl SchemaModule) -> Result<()> {
let loader = Loader::start(LoaderVariant::default());
if let Some(id) = &options.id {
return agent
.get_by_id(id.to_string())
.await
.map(|schema| pretty_print_obj(schema.schema));
return agent.get_by_id(id.to_string()).await.map(|schema| {
loader.stop();
pretty_print_obj(schema.schema)
});
}
if options.all {
return agent
.get_all()
.await
.map(|schemas| schemas.schema_ids.iter().for_each(|x| info!("{}", x)));
return agent.get_all().await.map(|schemas| {
loader.stop();
schemas.schema_ids.iter().for_each(|x| info!("{}", x))
});
}
match options
.commands
Expand All @@ -62,10 +64,10 @@ pub async fn parse_schema_args(options: &SchemaOptions, agent: impl SchemaModule
if options.attributes.is_empty() {
return Err(Error::RequiredAttributes.into());
}
agent
.create(options)
.await
.map(|schema_id| info!("{}", schema_id))
agent.create(options).await.map(|schema_id| {
loader.stop();
info!("{}", schema_id)
})
}
}
}
86 changes: 86 additions & 0 deletions cli/src/utils/loader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use core::time;
use std::io::{self, Write};
use std::sync::mpsc::{Receiver, Sender};

/// Macaroni
macro_rules! print_char_with_stop {
($char: expr, $timeout: expr, $inplace: expr, $receiver: expr) => {
Loader::print_char($char, $timeout, $inplace);
if $receiver.try_recv().is_ok() {
break;
}
};
}

/// All the types of loaders
pub enum LoaderVariant {
Spinner,
#[allow(dead_code)]
Dots,
}

/// Loader structure
pub struct Loader {
sender: Sender<bool>,
}

/// We implement default here so that the public api for calling the logger can just use this one
/// if unsure about which loader variant to call
impl Default for LoaderVariant {
fn default() -> Self {
LoaderVariant::Spinner
}
}

impl Loader {
/// Start a specific loader
pub fn start(loader_variant: LoaderVariant) -> Self {
let (sender, receiver) = std::sync::mpsc::channel::<bool>();
match loader_variant {
LoaderVariant::Spinner => Loader::loader_spinner(receiver),
LoaderVariant::Dots => Loader::loader_dots(receiver),
};
Loader { sender }
}

/// Stop the loader instance
pub fn stop(&self) {
eprintln!();
self.sender.send(false).unwrap();
}

/// prints a char to stdout with a delay and whether it should be inplace or
/// not
fn print_char(c: impl Into<String>, timeout: u64, inplace: bool) {
if inplace {
eprint!("{}\r", c.into());
} else {
eprint!("{}", c.into());
}
io::stdout().flush().unwrap();
std::thread::sleep(time::Duration::from_millis(timeout));
}

/// Spinning loader. Does inplace replacement.
fn loader_spinner(receiver: Receiver<bool>) {
let time_between = 50;

std::thread::spawn(move || loop {
print_char_with_stop!('|', time_between, true, receiver);
print_char_with_stop!('/', time_between * 2, true, receiver);
print_char_with_stop!('-', time_between * 3, true, receiver);
print_char_with_stop!('\\', time_between * 4, true, receiver);
print_char_with_stop!('/', time_between * 5, true, receiver);
print_char_with_stop!('-', time_between * 6, true, receiver);
print_char_with_stop!('\\', time_between * 7, true, receiver);
});
}

fn loader_dots(receiver: Receiver<bool>) {
let time_between = 100;

std::thread::spawn(move || loop {
print_char_with_stop!('.', time_between, false, receiver);
});
}
}
1 change: 1 addition & 0 deletions cli/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
pub mod logger;
pub mod config;
pub mod qr;
pub mod loader;

0 comments on commit cebbd1c

Please sign in to comment.