use std::io::Write;
use std::process::{Command, Stdio};
use std::str;
use chrono::{Date, DateTime, Utc, MAX_DATE, MIN_DATE};
use chrono_english::{parse_date_string, Dialect};
use color_eyre::Help;
use dialoguer::{Confirm, Editor, Input};
use syntect::highlighting::Style;
use syntect::util::as_24_bit_terminal_escaped;
use crate::errors::LostTheWay;
pub const END_ANSI: &str = "\x1b[0m";
pub const BOX: &str = "\u{25a0}";
pub const NAME: &str = "the-way";
pub const SEMICOLON: u8 = 59;
#[cfg(target_os = "linux")]
mod copy {
pub const COMMAND: &str = "xclip";
pub const ARGS: [&str; 3] = ["-in", "-selection", "clipboard"];
}
#[cfg(target_os = "macos")]
mod copy {
pub const COMMAND: &str = "pbcopy";
pub const ARGS: [&str; 0] = [];
}
#[cfg(target_os = "android")]
mod copy {
pub const COMMAND: &str = "termux-clipboard-set";
pub const ARGS: [&str; 0] = [];
}
pub fn copy_to_clipboard(text: &str) -> color_eyre::Result<()> {
let mut child = Command::new(copy::COMMAND)
.args(©::ARGS)
.stdin(Stdio::piped())
.spawn()
.map_err(|_| LostTheWay::ClipboardError {
message: format!("is {} available?", copy::COMMAND),
})?;
{
let stdin = child.stdin.as_mut().ok_or(LostTheWay::ClipboardError {
message: "Could not access stdin".to_string(),
})?;
stdin.write_all(text.as_bytes())?;
}
child.wait()?;
Ok(())
}
pub fn split_tags(input: &str) -> Vec<String> {
input
.split(' ')
.map(|word| word.trim().to_string())
.collect::<Vec<String>>()
}
pub fn u8_to_str(input: &[u8]) -> color_eyre::Result<String> {
Ok(str::from_utf8(input)?.to_owned())
}
pub fn split_indices_usize(index_list: &[u8]) -> color_eyre::Result<Vec<usize>> {
let index_list_string = str::from_utf8(index_list)?;
Ok(index_list_string
.split(str::from_utf8(&[SEMICOLON])?)
.map(str::parse)
.collect::<color_eyre::Result<Vec<_>, _>>()?)
}
pub fn make_indices_string(index_list: &[usize]) -> color_eyre::Result<Vec<u8>> {
Ok(index_list
.iter()
.map(|index| index.to_string())
.collect::<Vec<String>>()
.join(str::from_utf8(&[SEMICOLON])?)
.as_bytes()
.to_vec())
}
pub fn parse_date(date_string: &str) -> color_eyre::Result<Date<Utc>> {
if date_string.to_ascii_lowercase() == "today" {
Ok(Utc::now().date())
} else {
Ok(parse_date_string(date_string, Utc::now(), Dialect::Uk)?.date())
}
}
pub fn date_start(from_date: Option<Date<Utc>>) -> DateTime<Utc> {
match from_date {
Some(from_date) => from_date.and_hms(0, 0, 0),
None => MIN_DATE.and_hms(0, 0, 0),
}
}
pub fn date_end(to_date: Option<Date<Utc>>) -> DateTime<Utc> {
match to_date {
Some(to_date) => to_date.and_hms(23, 59, 59),
None => MAX_DATE.and_hms(23, 59, 59),
}
}
pub fn external_editor_input(default: Option<&str>, extension: &str) -> color_eyre::Result<String> {
Editor::new()
.extension(extension)
.edit(default.unwrap_or(""))
.suggestion("Set your default editor using the $EDITOR or $VISUAL environment variables")?
.ok_or(LostTheWay::EditorError)
.suggestion("Make sure to save next time if you want to record a snippet!")
}
pub fn user_input(
message: &str,
default: Option<&str>,
show_default: bool,
allow_empty: bool,
) -> color_eyre::Result<String> {
let theme = dialoguer::theme::ColorfulTheme::default();
match default {
Some(default) => {
let mut input = Input::with_theme(&theme);
input
.with_prompt(message)
.allow_empty(allow_empty)
.default(default.to_owned())
.show_default(false);
if show_default {
input.with_initial_text(default);
}
Ok(input.interact_text()?.trim().to_owned())
}
None => Ok(Input::<String>::with_theme(&theme)
.with_prompt(message)
.allow_empty(allow_empty)
.interact_text()?
.trim()
.to_owned()),
}
}
pub fn confirm(prompt: &str, default: bool) -> color_eyre::Result<bool> {
let theme = dialoguer::theme::ColorfulTheme::default();
Ok(Confirm::with_theme(&theme)
.with_prompt(prompt)
.default(default)
.show_default(false)
.interact()?)
}
pub fn get_spinner(message: &str) -> indicatif::ProgressBar {
let spinner = indicatif::ProgressBar::new_spinner();
spinner.set_message(message.to_owned());
spinner
}
pub fn highlight_string(line: &str, style: Style) -> String {
let mut s = as_24_bit_terminal_escaped(&[(style, line)], false);
s.push_str(END_ANSI);
s
}
pub fn highlight_strings(inputs: &[(Style, String)], bg: bool) -> String {
if bg {
let mut s = String::new();
for (style, line) in inputs {
s.push_str(&as_24_bit_terminal_escaped(&[(*style, line)], true));
s.push_str(END_ANSI);
}
s
} else {
as_24_bit_terminal_escaped(
&inputs
.iter()
.map(|(style, line)| (*style, line.as_ref()))
.collect::<Vec<_>>(),
false,
)
}
}