1mod list_impl;
2pub use list_impl::*;
3
4mod grid;
5pub use grid::Grid;
6
7mod table_lib;
8mod time_fmt;
9mod tree;
10mod tree_lib;
11
12use crate::color::{Color, StyleObj, Stylize};
13use crate::util::writable::{write_writable, FmtWrite, Writable};
14use crate::{
15 error::{DisplayError, DisplayResult, Result},
16 script::ScriptInfo,
17};
18use serde::Serialize;
19use std::borrow::Cow;
20use std::num::NonZeroUsize;
21use std::str::FromStr;
22
23#[derive(Clone, Copy, Debug)]
24struct LatestTxt(&'static str, &'static str);
25const SHORT_LATEST_TXT: LatestTxt = LatestTxt("*", "");
26const LONG_LATEST_TXT: LatestTxt = LatestTxt(" *", " ");
27
28fn style_name_w(
29 mut w: impl Writable,
30 plain: bool,
31 is_latest: bool,
32 latest_txt: LatestTxt,
33 color: Color,
34 name: &str,
35) -> Result<usize> {
36 let mut width = name.len();
37 if is_latest && !plain {
38 write_writable!(w, "{}", latest_txt.0.stylize().color(Color::Yellow).bold())?;
39 width += latest_txt.0.len();
40 } else {
41 write_writable!(w, "{}", latest_txt.1)?;
42 width += latest_txt.1.len();
43 }
44 let name = style(plain, name, |s| {
45 s.color(color).bold();
46 if is_latest {
47 s.underline();
48 }
49 });
50 write_writable!(w, "{}", name)?;
51 Ok(width)
52}
53
54fn style_name(
55 plain: bool,
56 is_latest: bool,
57 latest_txt: LatestTxt,
58 color: Color,
59 name: &str,
60) -> Result<(String, usize)> {
61 let mut s = String::new();
62 let width = style_name_w(FmtWrite(&mut s), plain, is_latest, latest_txt, color, name)?;
63 Ok((s, width))
64}
65
66fn extract_help(script: &ScriptInfo) -> String {
67 let mut buff = String::new();
68 fn inner(buff: &mut String, script: &ScriptInfo) -> Result {
69 let script_path = crate::path::open_script(&script.name, &script.ty, Some(true))?;
70 *buff = crate::util::read_file(&script_path)?;
71 Ok(())
72 }
73 match inner(&mut buff, script) {
74 Err(e) => {
75 log::warn!("讀取腳本失敗{},直接回空的幫助字串", e);
76 return String::new();
77 }
78 Ok(()) => (),
79 };
80 let mut helps = crate::extract_msg::extract_help_from_content(&buff);
81 helps.next().unwrap_or_default().to_owned()
82}
83
84fn exec_time_str(script: &ScriptInfo) -> Cow<'static, str> {
85 match &script.exec_time {
86 None => Cow::Borrowed("Never"),
87 Some(t) => Cow::Owned(format!("{} ({})", time_fmt::fmt(t), script.exec_count)),
88 }
89}
90
91#[derive(Debug, Clone, Copy, Eq, PartialEq)]
92pub enum DisplayIdentStyle {
93 File,
94 Name,
95 Normal,
96 NameAndFile,
97}
98#[derive(Debug, Eq, PartialEq)]
99pub enum DisplayStyle<T, U> {
100 Short(DisplayIdentStyle, U),
101 Long(T),
102}
103#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
104pub enum Grouping {
105 Tag,
106 Tree,
107 None,
108}
109impl Grouping {
110 pub fn is_none(self) -> bool {
111 self == Grouping::None
112 }
113}
114impl Default for Grouping {
115 fn default() -> Self {
116 Grouping::None
117 }
118}
119impl FromStr for Grouping {
120 type Err = DisplayError;
121 fn from_str(s: &str) -> DisplayResult<Self> {
122 let g = match s {
123 "tag" => Grouping::Tag,
124 "tree" => Grouping::Tree,
125 "none" => Grouping::None,
126 _ => unreachable!(),
127 };
128 Ok(g)
129 }
130}
131
132#[derive(Debug)]
133pub struct ListOptions<T = (), U = ()> {
134 pub grouping: Grouping,
135 pub plain: bool,
136 pub limit: Option<NonZeroUsize>,
137 pub display_style: DisplayStyle<T, U>,
138}
139
140#[inline]
141fn style<T: std::fmt::Display, F: for<'a> FnOnce(&'a mut StyleObj<T>)>(
142 plain: bool,
143 s: T,
144 f: F,
145) -> StyleObj<T> {
146 let mut s = s.stylize();
147 if !plain {
148 f(&mut s);
149 }
150 s
151}
152
153pub fn get_screen_width() -> u16 {
154 console::Term::stdout().size_checked().map_or(0, |s| s.1)
155}