Respect --json in mensa tags

See #4.
This commit is contained in:
Malte Tammena 2021-10-20 20:35:08 +02:00
parent fcf81a71db
commit ab9bd9801c
3 changed files with 87 additions and 43 deletions

View file

@ -63,10 +63,9 @@
use chrono::Duration;
use directories_next::ProjectDirs;
use lazy_static::lazy_static;
use serde::Serialize;
use structopt::StructOpt;
use strum::IntoEnumIterator;
use tracing::error;
use unicode_width::UnicodeWidthStr;
/// Colorizes the output.
///
@ -170,7 +169,9 @@ fn real_main() -> Result<()> {
let canteens = Canteen::fetch(&state)?;
Canteen::print_all(&state, &canteens);
}
Some(Command::Tags) => print_tags(&state),
Some(Command::Tags) => {
Tag::print_all(&state)?;
}
None => {
let cmd = MealsCommand::default();
let state = State::from(state, &cmd);
@ -181,38 +182,6 @@ fn real_main() -> Result<()> {
Ok(())
}
fn print_tags<Cmd>(state: &State<Cmd>) {
for tag in Tag::iter() {
println!();
const ID_WIDTH: usize = 4;
const TEXT_INDENT: &str = " ";
let emoji = if state.args.plain && tag.is_primary() {
format!("{:>width$}", "-", width = ID_WIDTH)
} else {
let emoji = tag.as_id(state);
let emoji_len = emoji.width();
format!(
"{}{}",
" ".repeat(ID_WIDTH.saturating_sub(emoji_len)),
emoji
)
};
let description_width = get_sane_terminal_dimensions().0;
let description = textwrap::fill(
tag.describe(),
textwrap::Options::new(description_width)
.initial_indent(TEXT_INDENT)
.subsequent_indent(TEXT_INDENT),
);
println!(
"{} {}\n{}",
color!(state: emoji; bright_yellow, bold),
color!(state: tag; bold),
color!(state: description; bright_black),
);
}
}
fn get_sane_terminal_dimensions() -> (usize, usize) {
terminal_size::terminal_size()
.map(|(w, h)| (w.0 as usize, h.0 as usize))
@ -221,3 +190,10 @@ fn get_sane_terminal_dimensions() -> (usize, usize) {
.log_warn()
.unwrap_or((80, 80))
}
fn print_json<T: Serialize>(value: &T) -> Result<()> {
let stdout = std::io::stdout();
let output = stdout.lock();
serde_json::to_writer_pretty(output, value)
.map_err(|why| Error::Serializing(why, "writing meals as json"))
}

View file

@ -10,8 +10,8 @@ use std::collections::HashSet;
use crate::{
cache::fetch_json,
config::{args::MealsCommand, MealsState, PriceTags},
error::{pass_info, Error, Result},
get_sane_terminal_dimensions,
error::{pass_info, Result},
get_sane_terminal_dimensions, print_json,
tag::Tag,
State, ENDPOINT,
};
@ -128,10 +128,7 @@ impl Meal {
let favs = state.get_favs_rule();
let meals = meals.iter().filter(|meal| filter.is_match(meal));
if state.args.json {
let stdout = std::io::stdout();
let output = stdout.lock();
serde_json::to_writer_pretty(output, &meals.collect::<Vec<_>>())
.map_err(|why| Error::Serializing(why, "writing meals as json"))
print_json(&meals.collect::<Vec<_>>())
} else {
for meal in meals {
let is_fav = favs.is_match(meal);

View file

@ -1,10 +1,16 @@
use std::collections::HashMap;
use lazy_static::lazy_static;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use regex::RegexSet;
use serde::{Deserialize, Serialize};
use strum::{Display, EnumIter};
use strum::{Display, EnumIter, IntoEnumIterator};
use unicode_width::UnicodeWidthStr;
use crate::config::State;
use crate::{config::State, error::Result, get_sane_terminal_dimensions, print_json};
const ID_WIDTH: usize = 4;
const TEXT_INDENT: &str = " ";
lazy_static! {
/// These must have the same order as the variants in the [`Tag`] enum.
@ -170,4 +176,69 @@ impl Tag {
}
}
}
/// Print this tag.
///
/// Does **not** respect `--json`, use [`Self::print_all`].
pub fn print<Cmd>(&self, state: &State<Cmd>) {
let emoji = if state.args.plain && self.is_primary() {
format!("{:>width$}", "-", width = ID_WIDTH)
} else {
let emoji = self.as_id(state);
let emoji_len = emoji.width();
format!(
"{}{}",
" ".repeat(ID_WIDTH.saturating_sub(emoji_len)),
emoji
)
};
let description_width = get_sane_terminal_dimensions().0;
let description = textwrap::fill(
self.describe(),
textwrap::Options::new(description_width)
.initial_indent(TEXT_INDENT)
.subsequent_indent(TEXT_INDENT),
);
println!(
"{} {}\n{}",
color!(state: emoji; bright_yellow, bold),
color!(state: self; bold),
color!(state: description; bright_black),
);
}
/// Print all tags.
pub fn print_all<Cmd>(state: &State<Cmd>) -> Result<()> {
if state.args.json {
Self::print_all_json(state)
} else {
for tag in Tag::iter() {
println!();
tag.print(state);
}
Ok(())
}
}
/// Print all tags as json.
///
/// This will result in a list of objects containing the following keys:
/// - id: An identifier, like 'Vegan' or '22'
/// - name: The name of the tag.
/// - desc: A simple description.
///
fn print_all_json<Cmd>(state: &State<Cmd>) -> Result<()> {
let tags: Vec<HashMap<&str, String>> = Tag::iter()
.map(|tag| {
vec![
("id", tag.as_id(state)),
("name", tag.to_string()),
("desc", tag.describe().to_owned()),
]
.into_iter()
.collect()
})
.collect();
print_json(&tags)
}
}