pub mod android;
#[cfg(target_os = "macos")]
pub mod apple;
pub mod cargo_mobile;
pub mod device_list;
use std::fmt::Debug;
use colored::Colorize as _;
use crate::util::{
self,
cli::{TextWrapper, colors},
};
#[derive(Clone, Copy, Debug)]
enum Label {
Victory,
Warning,
Error,
}
impl Label {
fn title_symbol(self) -> &'static str {
match self {
Self::Victory | Self::Warning => "✔",
Self::Error => "!",
}
}
fn item_symbol(self) -> &'static str {
match self {
Self::Victory => "•",
Self::Warning | Self::Error => "✗",
}
}
fn color(self) -> colored::Color {
match self {
Self::Victory => colors::VICTORY,
Self::Warning => colors::WARNING,
Self::Error => colors::ERROR,
}
}
fn format_title(self, title: &str) -> colored::ColoredString {
format!("[{}] {}", self.title_symbol(), title)
.color(self.color())
.bold()
}
fn format_item(self, msg: &str) -> colored::ColoredString {
let item = format!("{} {}", self.item_symbol(), msg);
match self {
Self::Victory => item.normal(),
_ => item.color(self.color()).bold(),
}
}
}
#[derive(Debug)]
struct Item {
label: Label,
msg: String,
}
impl<T: ToString, E: ToString> From<Result<T, E>> for Item {
fn from(result: Result<T, E>) -> Item {
Self::from_result(result)
}
}
impl Item {
fn new(label: Label, msg: impl ToString) -> Self {
Self {
label,
msg: msg.to_string(),
}
}
fn victory(msg: impl ToString) -> Self {
Self::new(Label::Victory, msg)
}
#[cfg(target_os = "macos")]
fn warning(msg: impl ToString) -> Self {
Self::new(Label::Warning, msg)
}
fn failure(msg: impl ToString) -> Self {
Self::new(Label::Error, msg)
}
fn from_result(result: Result<impl ToString, impl ToString>) -> Self {
util::unwrap_either(result.map(Self::victory).map_err(Self::failure))
}
fn is_warning(&self) -> bool {
matches!(self.label, Label::Warning)
}
fn is_failure(&self) -> bool {
matches!(self.label, Label::Error)
}
fn format(&self) -> colored::ColoredString {
self.label.format_item(&self.msg)
}
}
#[derive(Debug)]
pub struct Section {
title: String,
items: Vec<Item>,
}
impl Section {
fn new(title: impl ToString) -> Self {
Self {
title: title.to_string(),
items: Default::default(),
}
}
fn with_item(mut self, item: impl Into<Item>) -> Self {
self.items.push(item.into());
self
}
fn with_victory(self, victory: impl ToString) -> Self {
self.with_item(Item::victory(victory))
}
fn with_failure(self, failure: impl ToString) -> Self {
self.with_item(Item::failure(failure))
}
fn with_items(mut self, items: impl IntoIterator<Item = impl Into<Item>>) -> Self {
self.items.extend(items.into_iter().map(Into::into));
self
}
fn with_victories(self, victories: impl IntoIterator<Item = impl ToString>) -> Self {
self.with_items(victories.into_iter().map(Item::victory))
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
fn has_error(&self) -> bool {
self.items.iter().any(Item::is_failure)
}
fn has_warning(&self) -> bool {
self.items.iter().any(Item::is_warning)
}
fn label(&self) -> Label {
if self.has_error() {
Label::Error
} else if self.has_warning() {
Label::Warning
} else {
Label::Victory
}
}
pub fn print(&self, wrapper: &TextWrapper) {
static BULLET_INDENT: &str = " ";
static HANGING_INDENT: &str = " ";
let bullet_wrapper = TextWrapper(
wrapper
.clone()
.0
.initial_indent(BULLET_INDENT)
.subsequent_indent(HANGING_INDENT),
);
println!(
"\n{}",
wrapper.fill(&self.label().format_title(&self.title))
);
for report_bullet in &self.items {
println!("{}", bullet_wrapper.fill(&report_bullet.format()));
}
}
}