mirror of
https://github.com/MalteT/mensa.git
synced 2024-10-22 21:59:17 +02:00
Move tag.rs module, add basic --json
The `--json` flag is currently only respected by `mensa meals`. Relates to #4.
This commit is contained in:
parent
b5171922f6
commit
fcf81a71db
|
@ -7,7 +7,7 @@ use std::path::PathBuf;
|
|||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
meal::tag::Tag,
|
||||
tag::Tag,
|
||||
};
|
||||
|
||||
use super::PriceTags;
|
||||
|
@ -40,6 +40,10 @@ pub struct Args {
|
|||
)]
|
||||
pub color: ColorWhen,
|
||||
|
||||
/// Print plain json. Useful for shell scripts.
|
||||
#[structopt(long, global = true, takes_value = false)]
|
||||
pub json: bool,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
pub command: Option<Command>,
|
||||
}
|
||||
|
@ -92,7 +96,11 @@ pub struct MealsCommand {
|
|||
pub canteen_id: Option<usize>,
|
||||
|
||||
/// Specify which price tags should be displayed
|
||||
#[structopt(long, short, env = "MENSA_PRICES", possible_values = &PriceTags::variants())]
|
||||
#[structopt(long,
|
||||
short,
|
||||
env = "MENSA_PRICES",
|
||||
possible_values = &PriceTags::variants(),
|
||||
case_insensitive = true)]
|
||||
pub price: Option<Vec<PriceTags>>,
|
||||
|
||||
#[structopt(long, env = "MENSA_OVERWRITE_FILTER", takes_value = false)]
|
||||
|
|
|
@ -4,7 +4,8 @@ use std::convert::TryFrom;
|
|||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
meal::{tag::Tag, Meal},
|
||||
meal::Meal,
|
||||
tag::Tag,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -109,15 +109,18 @@ mod config;
|
|||
mod error;
|
||||
mod geoip;
|
||||
mod meal;
|
||||
mod tag;
|
||||
|
||||
use config::{
|
||||
args::{parse_human_date, Args, MealsCommand},
|
||||
use crate::{
|
||||
canteen::Canteen,
|
||||
config::{
|
||||
args::{parse_human_date, Args, Command, MealsCommand},
|
||||
State,
|
||||
},
|
||||
error::{Error, Result, ResultExt},
|
||||
meal::Meal,
|
||||
tag::Tag,
|
||||
};
|
||||
use error::{Error, Result, ResultExt};
|
||||
use meal::{tag::Tag, Meal};
|
||||
|
||||
use crate::{canteen::Canteen, config::args::Command};
|
||||
|
||||
const ENDPOINT: &str = "https://openmensa.org/api/v2";
|
||||
const MIN_TERM_WIDTH: usize = 20;
|
||||
|
@ -152,7 +155,7 @@ fn real_main() -> Result<()> {
|
|||
Some(Command::Meals(cmd)) => {
|
||||
let state = State::from(state, cmd);
|
||||
let meals = Meal::fetch(&state)?;
|
||||
Meal::print_all(&state, &meals);
|
||||
Meal::print_all(&state, &meals)?;
|
||||
}
|
||||
Some(Command::Tomorrow(cmd)) => {
|
||||
// Works like the meals command. But we replace the date with tomorrow!
|
||||
|
@ -160,7 +163,7 @@ fn real_main() -> Result<()> {
|
|||
cmd.date = parse_human_date("tomorrow").unwrap();
|
||||
let state = State::from(state, &cmd);
|
||||
let meals = Meal::fetch(&state)?;
|
||||
Meal::print_all(&state, &meals);
|
||||
Meal::print_all(&state, &meals)?;
|
||||
}
|
||||
Some(Command::Canteens(cmd)) => {
|
||||
let state = State::from(state, cmd);
|
||||
|
@ -172,7 +175,7 @@ fn real_main() -> Result<()> {
|
|||
let cmd = MealsCommand::default();
|
||||
let state = State::from(state, &cmd);
|
||||
let meals = Meal::fetch(&state)?;
|
||||
Meal::print_all(&state, &meals);
|
||||
Meal::print_all(&state, &meals)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -2,8 +2,7 @@ use chrono::Duration;
|
|||
use core::fmt;
|
||||
use itertools::Itertools;
|
||||
use lazy_static::lazy_static;
|
||||
use owo_colors::{OwoColorize, Stream};
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
@ -11,14 +10,12 @@ use std::collections::HashSet;
|
|||
use crate::{
|
||||
cache::fetch_json,
|
||||
config::{args::MealsCommand, MealsState, PriceTags},
|
||||
error::{pass_info, Result},
|
||||
get_sane_terminal_dimensions, State, ENDPOINT,
|
||||
error::{pass_info, Error, Result},
|
||||
get_sane_terminal_dimensions,
|
||||
tag::Tag,
|
||||
State, ENDPOINT,
|
||||
};
|
||||
|
||||
pub mod tag;
|
||||
|
||||
use self::tag::Tag;
|
||||
|
||||
const NAME_PRE: &str = " ╭───╴";
|
||||
const NAME_PRE_PLAIN: &str = " - ";
|
||||
const NAME_CONTINUE_PRE: &str = " ┊ ";
|
||||
|
@ -36,9 +33,10 @@ lazy_static! {
|
|||
static ref TTL_MEALS: Duration = Duration::hours(1);
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(from = "RawMeal")]
|
||||
pub struct Meal {
|
||||
#[serde(rename = "id")]
|
||||
pub _id: usize,
|
||||
pub name: String,
|
||||
pub tags: HashSet<Tag>,
|
||||
|
@ -57,7 +55,7 @@ struct RawMeal {
|
|||
category: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(debug, serde(deny_unknown_fields))]
|
||||
pub struct Prices {
|
||||
students: f32,
|
||||
|
@ -123,17 +121,24 @@ impl Meal {
|
|||
/// Print the given meals.
|
||||
///
|
||||
/// Thi will respect passed cli arguments and the configuration.
|
||||
pub fn print_all(state: &MealsState, meals: &[Self]) {
|
||||
pub fn print_all(state: &MealsState, meals: &[Self]) -> Result<()> {
|
||||
// Load the filter which is used to select which meals to print.
|
||||
let filter = state.get_filter();
|
||||
// Load the favourites which will be used for marking meals.
|
||||
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"))
|
||||
} else {
|
||||
for meal in meals {
|
||||
if filter.is_match(meal) {
|
||||
let is_fav = favs.is_match(meal);
|
||||
println!();
|
||||
meal.print(state, is_fav);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use lazy_static::lazy_static;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use regex::RegexSet;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{Display, EnumIter};
|
||||
|
||||
use crate::config::State;
|
||||
|
@ -38,6 +38,9 @@ lazy_static! {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
/// A tag describing a meal.
|
||||
///
|
||||
/// Contains allergy information, descriptions and categories.
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
|
@ -49,6 +52,7 @@ lazy_static! {
|
|||
PartialOrd,
|
||||
IntoPrimitive,
|
||||
TryFromPrimitive,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
EnumIter,
|
||||
Display,
|
||||
|
@ -84,6 +88,7 @@ pub enum Tag {
|
|||
}
|
||||
|
||||
impl Tag {
|
||||
/// Try deriving [`Tag`]s from the `raw` tag.
|
||||
pub fn parse_str(raw: &str) -> Vec<Self> {
|
||||
TAG_RE
|
||||
.matches(raw)
|
||||
|
@ -92,6 +97,9 @@ impl Tag {
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// Is this a primary tag?
|
||||
///
|
||||
/// Primary tags have an associated emoji and are not allergy information.
|
||||
pub fn is_primary(&self) -> bool {
|
||||
use Tag::*;
|
||||
match self {
|
||||
|
@ -102,10 +110,15 @@ impl Tag {
|
|||
}
|
||||
}
|
||||
|
||||
/// Is this **not** a primary tag?
|
||||
pub fn is_secondary(&self) -> bool {
|
||||
!self.is_primary()
|
||||
}
|
||||
|
||||
/// Describe this [`Tag`] with english words.
|
||||
///
|
||||
/// This should add information where the enum variant itself
|
||||
/// does not suffice.
|
||||
pub fn describe(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Alcohol => "Contains alcohol",
|
||||
|
@ -140,7 +153,8 @@ impl Tag {
|
|||
|
||||
/// This formats an identifier for this tag.
|
||||
///
|
||||
/// Will respect any settings given.
|
||||
/// Will respect any settings given, i.e. emojis will be used
|
||||
/// unless the output should be plain.
|
||||
pub fn as_id<Cmd>(&self, state: &State<Cmd>) -> String {
|
||||
match self {
|
||||
Self::Vegan => if_plain!(state: "🌱".into(), "Vegan".into()),
|
Loading…
Reference in a new issue