mirror of
https://github.com/nikolassv/bartib
synced 2025-03-02 05:47:11 +00:00
added time parameter to start and stop commands
This commit is contained in:
parent
22aab0f31d
commit
c757d92b38
6 changed files with 169 additions and 79 deletions
|
@ -23,17 +23,17 @@ pub enum ActivityError {
|
|||
}
|
||||
|
||||
impl Activity {
|
||||
pub fn start(project: String, description: String) -> Activity {
|
||||
pub fn start(project: String, description: String, time: Option<NaiveDateTime>) -> Activity {
|
||||
Activity {
|
||||
start: Local::now().naive_local(),
|
||||
start: time.unwrap_or_else(|| Local::now().naive_local()),
|
||||
end: None,
|
||||
project,
|
||||
description,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.end = Some(Local::now().naive_local());
|
||||
pub fn stop(&mut self, time: Option<NaiveDateTime>) {
|
||||
self.end = time.or_else(|| Some(Local::now().naive_local()));
|
||||
}
|
||||
|
||||
pub fn is_stopped(&self) -> bool {
|
||||
|
@ -174,12 +174,14 @@ fn split_with_escaped_delimeter(s: &str) -> StringSplitter {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use chrono::{Datelike, Timelike};
|
||||
use std::option::Option::None;
|
||||
|
||||
#[test]
|
||||
fn start() {
|
||||
let t = Activity::start(
|
||||
"test project".to_string(),
|
||||
"test description".to_string(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(t.description, "test description".to_string());
|
||||
assert_eq!(t.project, "test project".to_string());
|
||||
|
@ -191,8 +193,9 @@ mod tests {
|
|||
let mut t = Activity::start(
|
||||
"test project".to_string(),
|
||||
"test description".to_string(),
|
||||
None,
|
||||
);
|
||||
t.stop();
|
||||
t.stop(None);
|
||||
assert_ne!(t.end, None);
|
||||
}
|
||||
|
||||
|
@ -201,6 +204,7 @@ mod tests {
|
|||
let mut t = Activity::start(
|
||||
"test project| 1".to_string(),
|
||||
"test\\description".to_string(),
|
||||
None,
|
||||
);
|
||||
t.start = NaiveDateTime::parse_from_str("2021-02-16 16:14", conf::FORMAT_DATETIME).unwrap();
|
||||
assert_eq!(
|
||||
|
@ -282,8 +286,9 @@ mod tests {
|
|||
let mut t = Activity::start(
|
||||
"ex\\ample\\\\pro|ject".to_string(),
|
||||
"e\\\\xam|||ple tas\t\t\nk".to_string(),
|
||||
None,
|
||||
);
|
||||
t.stop();
|
||||
t.stop(None);
|
||||
let t2 = Activity::from_str(format!("{}", t).as_str()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use anyhow::{Context, Result};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::str::FromStr;
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::activity;
|
||||
|
||||
|
@ -49,7 +49,8 @@ impl Line {
|
|||
|
||||
// reads the content of a file to a vector of lines
|
||||
pub fn get_file_content(file_name: &str) -> Result<Vec<Line>> {
|
||||
let file_handler = File::open(file_name).context(format!("Could not read from file: {}", file_name))?;
|
||||
let file_handler =
|
||||
File::open(file_name).context(format!("Could not read from file: {}", file_name))?;
|
||||
let reader = BufReader::new(file_handler);
|
||||
|
||||
let lines = reader
|
||||
|
|
71
src/lib.rs
71
src/lib.rs
|
@ -1,10 +1,9 @@
|
|||
use anyhow::{anyhow, bail, Context, Result, Error};
|
||||
use chrono::{naive, NaiveDate};
|
||||
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||
use chrono::{naive, NaiveDate, NaiveDateTime};
|
||||
|
||||
use crate::bartib_file::Line;
|
||||
use crate::activity::Activity;
|
||||
use crate::bartib_file::Line;
|
||||
use std::process::Command;
|
||||
|
||||
mod activity;
|
||||
pub mod bartib_file;
|
||||
pub mod conf;
|
||||
|
@ -20,24 +19,37 @@ pub struct ActivityFilter {
|
|||
}
|
||||
|
||||
// starts a new activity
|
||||
pub fn start(file_name: &str, project_name: &str, activity_description: &str) -> Result<()>{
|
||||
pub fn start(
|
||||
file_name: &str,
|
||||
project_name: &str,
|
||||
activity_description: &str,
|
||||
time: Option<NaiveDateTime>,
|
||||
) -> Result<()> {
|
||||
let mut file_content: Vec<Line> = Vec::new();
|
||||
|
||||
if let Ok(mut previous_file_content) = bartib_file::get_file_content(file_name) {
|
||||
// if we start a new activities programaticly, we stop all other activities first.
|
||||
// However, we must not assume that there is always only one activity
|
||||
// running as the user may have started activities manually
|
||||
stop_all_running_activities(&mut previous_file_content);
|
||||
stop_all_running_activities(&mut previous_file_content, time);
|
||||
|
||||
file_content.append(&mut previous_file_content);
|
||||
}
|
||||
|
||||
let activity = activity::Activity::start(project_name.to_string(), activity_description.to_string());
|
||||
let activity = activity::Activity::start(
|
||||
project_name.to_string(),
|
||||
activity_description.to_string(),
|
||||
time,
|
||||
);
|
||||
|
||||
save_new_activity(file_name, &mut file_content, activity)
|
||||
}
|
||||
|
||||
fn save_new_activity(file_name: &str, file_content: &mut Vec<Line>, activity: Activity) -> Result<(), Error> {
|
||||
fn save_new_activity(
|
||||
file_name: &str,
|
||||
file_content: &mut Vec<Line>,
|
||||
activity: Activity,
|
||||
) -> Result<(), Error> {
|
||||
println!(
|
||||
"Started activity: \"{}\" ({}) at {}",
|
||||
activity.description,
|
||||
|
@ -46,18 +58,20 @@ fn save_new_activity(file_name: &str, file_content: &mut Vec<Line>, activity: Ac
|
|||
);
|
||||
|
||||
file_content.push(bartib_file::Line::for_activity(activity));
|
||||
bartib_file::write_to_file(file_name, &file_content).context(format!("Could not write to file: {}", file_name))
|
||||
bartib_file::write_to_file(file_name, &file_content)
|
||||
.context(format!("Could not write to file: {}", file_name))
|
||||
}
|
||||
|
||||
// stops all currently running activities
|
||||
pub fn stop(file_name: &str) -> Result<()>{
|
||||
pub fn stop(file_name: &str, time: Option<NaiveDateTime>) -> Result<()> {
|
||||
let mut file_content = bartib_file::get_file_content(file_name)?;
|
||||
stop_all_running_activities(&mut file_content);
|
||||
bartib_file::write_to_file(file_name, &file_content).context(format!("Could not write to file: {}", file_name))
|
||||
stop_all_running_activities(&mut file_content, time);
|
||||
bartib_file::write_to_file(file_name, &file_content)
|
||||
.context(format!("Could not write to file: {}", file_name))
|
||||
}
|
||||
|
||||
// lists all currently runninng activities.
|
||||
pub fn list_running(file_name: &str) -> Result<()>{
|
||||
pub fn list_running(file_name: &str) -> Result<()> {
|
||||
let file_content = bartib_file::get_file_content(file_name)?;
|
||||
let running_activities = get_running_activities(&file_content);
|
||||
output::list_running_activities(&running_activities);
|
||||
|
@ -98,7 +112,7 @@ pub fn list(file_name: &str, filter: ActivityFilter, do_group_activities: bool)
|
|||
pub fn list_projects(file_name: &str) -> Result<()> {
|
||||
let file_content = bartib_file::get_file_content(file_name)?;
|
||||
|
||||
let mut all_projects : Vec<&String> = get_activities(&file_content)
|
||||
let mut all_projects: Vec<&String> = get_activities(&file_content)
|
||||
.map(|activity| &activity.project)
|
||||
.collect();
|
||||
|
||||
|
@ -128,18 +142,26 @@ pub fn display_last_activity(file_name: &str) -> Result<()> {
|
|||
}
|
||||
|
||||
// continue last activity
|
||||
pub fn continue_last_activity(file_name: &str, project_name: Option<&str>, activity_description: Option<&str>) -> Result<()> {
|
||||
pub fn continue_last_activity(
|
||||
file_name: &str,
|
||||
project_name: Option<&str>,
|
||||
activity_description: Option<&str>,
|
||||
time: Option<NaiveDateTime>,
|
||||
) -> Result<()> {
|
||||
let mut file_content = bartib_file::get_file_content(file_name)?;
|
||||
|
||||
let optional_last_activity = get_last_activity_by_start(&file_content)
|
||||
.or(get_last_activity(&file_content));
|
||||
let optional_last_activity =
|
||||
get_last_activity_by_start(&file_content).or(get_last_activity(&file_content));
|
||||
|
||||
if let Some(last_activity) = optional_last_activity {
|
||||
let new_activity = activity::Activity::start(
|
||||
project_name.unwrap_or(&last_activity.project).to_string(),
|
||||
activity_description.unwrap_or(&last_activity.description).to_string()
|
||||
activity_description
|
||||
.unwrap_or(&last_activity.description)
|
||||
.to_string(),
|
||||
time,
|
||||
);
|
||||
stop_all_running_activities(&mut file_content);
|
||||
stop_all_running_activities(&mut file_content, time);
|
||||
save_new_activity(file_name, &mut file_content, new_activity)
|
||||
} else {
|
||||
bail!("No activity has been started before.")
|
||||
|
@ -155,9 +177,7 @@ pub fn start_editor(file_name: &str, optional_editor_command: Option<&str>) -> R
|
|||
child.wait().context("editor did not execute")?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
Err(anyhow!(e))
|
||||
}
|
||||
Err(e) => Err(anyhow!(e)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,11 +199,14 @@ fn get_index_of_first_element(length: usize, sub: Option<usize>) -> usize {
|
|||
}
|
||||
}
|
||||
|
||||
fn stop_all_running_activities(file_content: &mut [bartib_file::Line]) {
|
||||
fn stop_all_running_activities(
|
||||
file_content: &mut [bartib_file::Line],
|
||||
time: Option<NaiveDateTime>,
|
||||
) {
|
||||
for line in file_content {
|
||||
if let Ok(activity) = &mut line.activity {
|
||||
if !activity.is_stopped() {
|
||||
activity.stop();
|
||||
activity.stop(time);
|
||||
println!(
|
||||
"Stopped activity: \"{}\" ({}) started at {} ({})",
|
||||
activity.description,
|
||||
|
|
81
src/main.rs
81
src/main.rs
|
@ -1,15 +1,21 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use chrono::{NaiveDate, Local, Duration};
|
||||
use chrono::{Duration, Local, NaiveDate, NaiveTime};
|
||||
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use nu_ansi_term::enable_ansi_support;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
|
||||
#[cfg(windows)]
|
||||
if let Err(e) = enable_ansi_support() {
|
||||
println!("Could not enable ansi support! Errorcode: {}", e);
|
||||
}
|
||||
|
||||
let arg_time = Arg::with_name("time")
|
||||
.short("t")
|
||||
.long("time")
|
||||
.value_name("TIME")
|
||||
.help("the time for changing the activity status (HH:MM)")
|
||||
.takes_value(true);
|
||||
|
||||
let matches = App::new("bartib")
|
||||
.version("0.1")
|
||||
.author("Nikolas Schmidt-Voigt <nikolas.schmidt-voigt@posteo.de>")
|
||||
|
@ -42,7 +48,8 @@ fn main() -> Result<()> {
|
|||
.help("a description of the new activity")
|
||||
.required(true)
|
||||
.takes_value(true),
|
||||
),
|
||||
)
|
||||
.arg(&arg_time),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("continue")
|
||||
|
@ -60,9 +67,14 @@ fn main() -> Result<()> {
|
|||
.value_name("DESCRIPTION")
|
||||
.help("a description of the new activity")
|
||||
.takes_value(true),
|
||||
),
|
||||
)
|
||||
.arg(&arg_time),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("stop")
|
||||
.about("stops all currently running activities")
|
||||
.arg(&arg_time),
|
||||
)
|
||||
.subcommand(SubCommand::with_name("stop").about("stops all currently running activities"))
|
||||
.subcommand(
|
||||
SubCommand::with_name("current").about("lists all currently running activities"),
|
||||
)
|
||||
|
@ -110,7 +122,7 @@ fn main() -> Result<()> {
|
|||
.help("show activities of the current day")
|
||||
.required(false)
|
||||
.conflicts_with_all(&["from_date", "to_date", "date", "yesterday"])
|
||||
.takes_value(false)
|
||||
.takes_value(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("yesterday")
|
||||
|
@ -118,7 +130,7 @@ fn main() -> Result<()> {
|
|||
.help("show yesterdays activities")
|
||||
.required(false)
|
||||
.conflicts_with_all(&["from_date", "to_date", "date", "today"])
|
||||
.takes_value(false)
|
||||
.takes_value(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no_grouping")
|
||||
|
@ -126,12 +138,8 @@ fn main() -> Result<()> {
|
|||
.help("do not group activities by date in list"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("last").about("displays last finished acitivity")
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("projects").about("list all projects")
|
||||
)
|
||||
.subcommand(SubCommand::with_name("last").about("displays last finished acitivity"))
|
||||
.subcommand(SubCommand::with_name("projects").about("list all projects"))
|
||||
.subcommand(
|
||||
SubCommand::with_name("edit")
|
||||
.about("opens the activity log in an editor")
|
||||
|
@ -142,7 +150,7 @@ fn main() -> Result<()> {
|
|||
.help("the command to start your prefered text editor")
|
||||
.env("EDITOR")
|
||||
.takes_value(true),
|
||||
)
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
|
@ -157,16 +165,25 @@ fn run_subcommand(matches: &ArgMatches, file_name: &str) -> Result<()> {
|
|||
("start", Some(sub_m)) => {
|
||||
let project_name = sub_m.value_of("project").unwrap();
|
||||
let activity_description = sub_m.value_of("description").unwrap();
|
||||
let time = get_time_argument_or_ignore(sub_m.value_of("time"), "-t/--time")
|
||||
.map(|t| Local::today().naive_local().and_time(t));
|
||||
|
||||
bartib::start(file_name, project_name, activity_description)
|
||||
},
|
||||
bartib::start(file_name, project_name, activity_description, time)
|
||||
}
|
||||
("continue", Some(sub_m)) => {
|
||||
let project_name = sub_m.value_of("project");
|
||||
let activity_description = sub_m.value_of("description");
|
||||
let time = get_time_argument_or_ignore(sub_m.value_of("time"), "-t/--time")
|
||||
.map(|t| Local::today().naive_local().and_time(t));
|
||||
|
||||
bartib::continue_last_activity(file_name, project_name, activity_description)
|
||||
},
|
||||
("stop", Some(_)) => bartib::stop(file_name),
|
||||
bartib::continue_last_activity(file_name, project_name, activity_description, time)
|
||||
}
|
||||
("stop", Some(sub_m)) => {
|
||||
let time = get_time_argument_or_ignore(sub_m.value_of("time"), "-t/--time")
|
||||
.map(|t| Local::today().naive_local().and_time(t));
|
||||
|
||||
bartib::stop(file_name, time)
|
||||
}
|
||||
("current", Some(_)) => bartib::list_running(file_name),
|
||||
("list", Some(sub_m)) => {
|
||||
let mut filter = bartib::ActivityFilter {
|
||||
|
@ -189,13 +206,13 @@ fn run_subcommand(matches: &ArgMatches, file_name: &str) -> Result<()> {
|
|||
|
||||
let do_group_activities = !sub_m.is_present("no_grouping") && !filter.date.is_some();
|
||||
bartib::list(file_name, filter, do_group_activities)
|
||||
},
|
||||
}
|
||||
("projects", Some(_)) => bartib::list_projects(file_name),
|
||||
("last", Some(_)) => bartib::display_last_activity(file_name),
|
||||
("edit", Some(sub_m)) => {
|
||||
let optional_editor_command = sub_m.value_of("editor");
|
||||
bartib::start_editor(file_name, optional_editor_command)
|
||||
},
|
||||
}
|
||||
_ => bail!("Unknown command"),
|
||||
}
|
||||
}
|
||||
|
@ -242,3 +259,25 @@ fn get_date_argument_or_ignore(
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_time_argument_or_ignore(
|
||||
time_argument: Option<&str>,
|
||||
argument_name: &str,
|
||||
) -> Option<NaiveTime> {
|
||||
if let Some(time_string) = time_argument {
|
||||
let parsing_result = NaiveTime::parse_from_str(time_string, bartib::conf::FORMAT_TIME);
|
||||
|
||||
match parsing_result {
|
||||
Ok(date) => Some(date),
|
||||
Err(parsing_error) => {
|
||||
println!(
|
||||
"Can not parse \"{}\" as time. Argument for {} is ignored ({})",
|
||||
time_string, argument_name, parsing_error
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use nu_ansi_term::Color;
|
||||
use chrono::NaiveDate;
|
||||
use nu_ansi_term::Color;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::activity;
|
||||
|
@ -45,15 +45,21 @@ pub fn list_activities_grouped_by_date(activities: &[&activity::Activity]) {
|
|||
"Duration".to_string(),
|
||||
]);
|
||||
|
||||
group_activities_by_date(activities).iter()
|
||||
.map(|(date, activity_list)| create_activites_group(&format!("{}", date), activity_list.as_slice()))
|
||||
group_activities_by_date(activities)
|
||||
.iter()
|
||||
.map(|(date, activity_list)| {
|
||||
create_activites_group(&format!("{}", date), activity_list.as_slice())
|
||||
})
|
||||
.for_each(|g| activity_table.add_group(g));
|
||||
|
||||
println!("\n{}", activity_table);
|
||||
}
|
||||
|
||||
fn create_activites_group(title: &str, activities: &[&activity::Activity]) -> table::Group {
|
||||
let rows = activities.iter().map(|a| get_activity_table_row(&a, false)).collect();
|
||||
fn create_activites_group(title: &str, activities: &[&activity::Activity]) -> table::Group {
|
||||
let rows = activities
|
||||
.iter()
|
||||
.map(|a| get_activity_table_row(&a, false))
|
||||
.collect();
|
||||
table::Group::new(Some(title.to_string()), rows)
|
||||
}
|
||||
|
||||
|
@ -62,17 +68,16 @@ pub fn list_running_activities(running_activities: &[&activity::Activity]) {
|
|||
if running_activities.is_empty() {
|
||||
println!("No Activity is currently running");
|
||||
} else {
|
||||
let mut activity_table =
|
||||
table::Table::new(vec![
|
||||
"Started At".to_string(),
|
||||
"Description".to_string(),
|
||||
"Project".to_string(),
|
||||
"Duration".to_string()
|
||||
]);
|
||||
let mut activity_table = table::Table::new(vec![
|
||||
"Started At".to_string(),
|
||||
"Description".to_string(),
|
||||
"Project".to_string(),
|
||||
"Duration".to_string(),
|
||||
]);
|
||||
|
||||
running_activities
|
||||
.iter()
|
||||
.map(|activity | {
|
||||
.map(|activity| {
|
||||
table::Row::new(vec![
|
||||
activity.start.format(conf::FORMAT_DATETIME).to_string(),
|
||||
activity.description.clone(),
|
||||
|
@ -92,7 +97,10 @@ pub fn display_single_activity(activity: &activity::Activity) {
|
|||
|
||||
if let Some(end) = activity.end {
|
||||
println!("End: {}", end.format(conf::FORMAT_DATETIME));
|
||||
println!("Duration: {}", format_util::format_duration(&activity.get_duration()));
|
||||
println!(
|
||||
"Duration: {}",
|
||||
format_util::format_duration(&activity.get_duration())
|
||||
);
|
||||
}
|
||||
|
||||
println!("Project: {}", activity.project);
|
||||
|
|
38
src/table.rs
38
src/table.rs
|
@ -57,7 +57,8 @@ impl Table {
|
|||
}
|
||||
|
||||
fn get_all_rows(&self) -> Vec<&Row> {
|
||||
self.groups.iter()
|
||||
self.groups
|
||||
.iter()
|
||||
.flat_map(|g| g.rows.as_slice())
|
||||
.chain(self.rows.as_slice())
|
||||
.collect()
|
||||
|
@ -67,7 +68,8 @@ impl Table {
|
|||
let mut column_width: Vec<usize> = self.header.iter().map(|e| e.chars().count()).collect();
|
||||
|
||||
for row in self.get_all_rows() {
|
||||
row.content.iter()
|
||||
row.content
|
||||
.iter()
|
||||
.map(|cell| cell.chars().count())
|
||||
.enumerate()
|
||||
.for_each(|(i, char_count)| {
|
||||
|
@ -87,7 +89,12 @@ impl fmt::Display for Table {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let column_width = self.get_column_width();
|
||||
|
||||
write_cells(f, &self.header, &column_width, Some(Style::new().underline()))?;
|
||||
write_cells(
|
||||
f,
|
||||
&self.header,
|
||||
&column_width,
|
||||
Some(Style::new().underline()),
|
||||
)?;
|
||||
writeln!(f)?;
|
||||
|
||||
for row in &self.rows {
|
||||
|
@ -102,7 +109,11 @@ impl fmt::Display for Table {
|
|||
}
|
||||
}
|
||||
|
||||
fn write_group(f: &mut fmt::Formatter<'_>, group: &Group, column_width: &Vec<usize>) -> fmt::Result {
|
||||
fn write_group(
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
group: &Group,
|
||||
column_width: &Vec<usize>,
|
||||
) -> fmt::Result {
|
||||
let empty_string = "".to_string();
|
||||
let title = group.title.as_ref().unwrap_or(&empty_string);
|
||||
|
||||
|
@ -113,10 +124,10 @@ fn write_group(f: &mut fmt::Formatter<'_>, group: &Group, column_width: &Vec<usi
|
|||
write_row(f, row, &column_width)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_row(f: &mut fmt::Formatter<'_>, row: &Row, column_width : &Vec<usize>) -> fmt::Result {
|
||||
fn write_row(f: &mut fmt::Formatter<'_>, row: &Row, column_width: &Vec<usize>) -> fmt::Result {
|
||||
write_cells(f, &row.content, &column_width, row.style)?;
|
||||
writeln!(f)?;
|
||||
Ok(())
|
||||
|
@ -128,7 +139,8 @@ fn write_cells<T: AsRef<str> + std::fmt::Display>(
|
|||
column_width: &[usize],
|
||||
style: Option<Style>,
|
||||
) -> fmt::Result {
|
||||
let cells_with_width : Vec<(Option<&usize>, &str)> = cells.iter()
|
||||
let cells_with_width: Vec<(Option<&usize>, &str)> = cells
|
||||
.iter()
|
||||
.map(|cell| cell.as_ref())
|
||||
.enumerate()
|
||||
.map(|(i, cell)| (column_width.get(i), cell))
|
||||
|
@ -152,11 +164,13 @@ fn write_with_width_and_style(
|
|||
let style_suffix = opt_style.map_or("".to_string(), |style| style.suffix().to_string());
|
||||
let width = opt_width.unwrap_or(&content_length);
|
||||
|
||||
write!(f, "{prefix}{content:<width$}{suffix} ",
|
||||
prefix = style_prefix,
|
||||
content = content,
|
||||
width = width,
|
||||
suffix = style_suffix
|
||||
write!(
|
||||
f,
|
||||
"{prefix}{content:<width$}{suffix} ",
|
||||
prefix = style_prefix,
|
||||
content = content,
|
||||
width = width,
|
||||
suffix = style_suffix
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue