diff options
author | Marcel Müller <neikos@neikos.email> | 2023-04-13 09:46:39 +0200 |
---|---|---|
committer | Marcel Müller <neikos@neikos.email> | 2023-04-13 09:46:39 +0200 |
commit | 8c434e364533060fd9b42255b725a7fad1a4ef71 (patch) | |
tree | d5d2a79defd97f015b990bd30454509b55a69a4c | |
parent | dd07d63f9a15c7a27c5f366deb457c841578fff7 (diff) |
Add terminal output
Signed-off-by: Marcel Müller <neikos@neikos.email>
-rw-r--r-- | Cargo.lock | 317 | ||||
-rw-r--r-- | Cargo.toml | 12 | ||||
-rw-r--r-- | src/bin/describe_types.rs | 16 | ||||
-rw-r--r-- | src/render/markdown.rs (renamed from src/render.rs) | 0 | ||||
-rw-r--r-- | src/render/mod.rs | 9 | ||||
-rw-r--r-- | src/render/terminal.rs | 176 | ||||
-rw-r--r-- | tests/complex_derive.rs | 3 |
7 files changed, 526 insertions, 7 deletions
@@ -43,6 +43,12 @@ dependencies = [ ] [[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -155,6 +161,107 @@ dependencies = [ ] [[package]] +name = "coolor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4d7a805ca0d92f8c61a31c809d4323fdaa939b0b440e544d21db7797c5aaad" +dependencies = [ + "crossterm", +] + +[[package]] +name = "crossbeam" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +dependencies = [ + "cfg-if", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossterm" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +dependencies = [ + "winapi", +] + +[[package]] name = "errno" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -278,6 +385,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" [[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -296,10 +413,49 @@ dependencies = [ ] [[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimad" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b136454924e4d020e55c4992e07c105b40d5c41b84662862f0e15bc0a2efef" +dependencies = [ + "once_cell", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df031e117bca634c262e9bd3173776844b6c17a90b3741c9163663b4385af76" +dependencies = [ + "windows-sys 0.45.0", +] + +[[package]] name = "once_cell" -version = "1.13.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "online_description_generator" @@ -315,12 +471,46 @@ dependencies = [ ] [[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] name = "percent-encoding" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] +name = "pretty" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e6db26707236c991e87e751cb259ac03a3930ee4f79af75977a148811c89c0" +dependencies = [ + "arrayvec", + "typed-arena", + "unicode-segmentation", +] + +[[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -363,6 +553,15 @@ dependencies = [ ] [[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] name = "rustix" version = "0.37.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -383,6 +582,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] name = "serde" version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -414,6 +619,42 @@ dependencies = [ ] [[package]] +name = "signal-hook" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -442,6 +683,16 @@ dependencies = [ ] [[package]] +name = "term_size" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +dependencies = [ + "libc", + "winapi", +] + +[[package]] name = "termcolor" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -451,6 +702,40 @@ dependencies = [ ] [[package]] +name = "termimad" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749b18b17745261a883ab334d931adffc8c5e07e71c73a861e77124557e7b41f" +dependencies = [ + "coolor", + "crossbeam", + "crossterm", + "minimad", + "thiserror", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.10", +] + +[[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -486,8 +771,12 @@ version = "0.3.0" dependencies = [ "clap", "indexmap", + "nu-ansi-term", + "pretty", "serde", "serde_json", + "term_size", + "termimad", "trybuild", "type_description_derive", "url", @@ -504,6 +793,12 @@ dependencies = [ ] [[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + +[[package]] name = "unicode-bidi" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -531,6 +826,18 @@ dependencies = [ ] [[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] name = "url" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -554,6 +861,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] name = "wasm-bindgen" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -18,16 +18,22 @@ name = "describe_types" required-features = ["bin"] [features] -default = ["render"] -render = ["indexmap"] -bin = ["clap", "serde_json"] +default = [] +render = ["render_markdown", "render_terminal"] +render_markdown = ["indexmap"] +render_terminal = ["termimad", "pretty", "nu-ansi-term"] +bin = ["render", "clap", "serde_json", "term_size"] url = ["dep:url"] [dependencies] clap = { version = "4.2.1", features = ["derive"], optional = true } indexmap = { version = "1.9.3", optional = true } +nu-ansi-term = { version = "0.47.0", optional = true } +pretty = { version = "0.12.0", optional = true } serde = { version = "1.0.159", features = ["derive"] } serde_json = { version = "1.0.95", optional = true } +term_size = { version = "0.3.2", optional = true } +termimad = { version = "0.23.0", optional = true } type_description_derive = { version = "0.3.0", path = "type_description_derive" } url = { version = "2", optional = true } diff --git a/src/bin/describe_types.rs b/src/bin/describe_types.rs index 8d0e86b..ef24738 100644 --- a/src/bin/describe_types.rs +++ b/src/bin/describe_types.rs @@ -7,17 +7,19 @@ use std::{error::Error, fmt::Display}; use clap::{Parser, ValueEnum}; -use type_description::{render::render_to_markdown, TypeDescription}; +use type_description::{render::render_to_markdown, render::render_to_terminal, TypeDescription}; #[derive(Debug, ValueEnum, PartialEq, Clone, Copy)] enum OutputFormat { Markdown, + Terminal, } impl Display for OutputFormat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { OutputFormat::Markdown => write!(f, "markdown"), + OutputFormat::Terminal => write!(f, "terminal"), } } } @@ -25,7 +27,7 @@ impl Display for OutputFormat { #[derive(Parser, Debug)] #[clap(author, version, about)] struct Args { - #[clap(short, long, value_parser, default_value_t = OutputFormat::Markdown)] + #[clap(short, long, value_parser)] output_format: OutputFormat, } @@ -38,6 +40,16 @@ fn main() -> Result<(), Box<dyn Error>> { OutputFormat::Markdown => { println!("{}", render_to_markdown(&input)?); } + OutputFormat::Terminal => { + let terminal_width = term_size::dimensions().map(|(w, _)| w).unwrap_or(80); + let arena = pretty::Arena::new(); + + let rendered_doc = render_to_terminal(&input, &arena); + + let mut output = String::new(); + rendered_doc.render_fmt(terminal_width, &mut output)?; + println!("{}", output); + } } Ok(()) diff --git a/src/render.rs b/src/render/markdown.rs index 9c67cb2..9c67cb2 100644 --- a/src/render.rs +++ b/src/render/markdown.rs diff --git a/src/render/mod.rs b/src/render/mod.rs new file mode 100644 index 0000000..8d29f42 --- /dev/null +++ b/src/render/mod.rs @@ -0,0 +1,9 @@ +#[cfg(feature = "render_markdown")] +mod markdown; +#[cfg(feature = "render_markdown")] +pub use markdown::*; + +#[cfg(feature = "render_terminal")] +mod terminal; +#[cfg(feature = "render_terminal")] +pub use terminal::*; diff --git a/src/render/terminal.rs b/src/render/terminal.rs new file mode 100644 index 0000000..98c65cc --- /dev/null +++ b/src/render/terminal.rs @@ -0,0 +1,176 @@ +use nu_ansi_term::Color; +use pretty::Arena; +use pretty::Doc; +use pretty::DocAllocator; +use pretty::Pretty; +use pretty::RefDoc; +use termimad::MadSkin; + +use crate::EnumVariantRepresentation; +use crate::TypeDescription; +use crate::TypeEnumKind; +use crate::TypeKind; + +/// Get a [`RefDoc`](pretty::RefDoc) which can be used to write the documentation of this +pub fn render_to_terminal<'a>(desc: &'a TypeDescription, arena: &'a Arena<'a>) -> RefDoc<'a> { + let mut doc = arena.nil(); + + if !matches!(desc.kind(), TypeKind::Wrapped(_)) && desc.doc().is_none() { + doc = doc + .append(Color::LightBlue.bold().paint(desc.name()).to_string()) + .append(arena.space()) + .append(match desc.kind() { + TypeKind::Bool + | TypeKind::Integer { .. } + | TypeKind::Float { .. } + | TypeKind::String + | TypeKind::Wrapped(_) + | TypeKind::Array(_) + | TypeKind::HashMap { .. } => arena.nil(), + TypeKind::Struct(_) => { + arena.text(Color::Blue.dimmed().paint("[Table]").to_string()) + } + TypeKind::Enum(_, _) => { + arena.text(Color::Green.dimmed().paint("[Enum]").to_string()) + } + }) + .append(arena.hardline()); + } + + let skin = MadSkin::default_dark(); + let render_markdown = |text: &str| { + let rendered = skin.text(text, None).to_string(); + arena.intersperse( + rendered.split('\n').map(|t| { + arena.intersperse( + t.split(char::is_whitespace).map(|t| t.to_string()), + arena.softline(), + ) + }), + arena.hardline(), + ) + }; + + if let Some(conf_doc) = desc.doc() { + doc = doc.append(render_markdown(conf_doc)); + } + + match desc.kind() { + TypeKind::Bool | TypeKind::Integer { .. } | TypeKind::Float { .. } | TypeKind::String => (), + TypeKind::Struct(stc) => { + doc = doc + .append(arena.hardline()) + .append(Color::Blue.paint("[Members]").to_string()) + .append(arena.hardline()) + .append(arena.intersperse( + stc.iter().map(|ev| { + let member_name = ev.name(); + let member_doc = ev.doc(); + let member_conf = ev.kind(); + let mut doc = arena.nil(); + + if let Some(member_doc) = member_doc { + doc = doc.append(render_markdown(member_doc)); + } + doc.append(arena.text(Color::Blue.bold().paint(member_name).to_string())) + .append(": ") + .append( + Pretty::pretty(render_to_terminal(member_conf, arena), arena) + .nest(4), + ) + }), + Doc::hardline(), + )) + } + TypeKind::Enum(enum_kind, variants) => { + doc = doc + .append(arena.hardline()) + .append(Color::Green.paint("One of:").to_string()) + .append(arena.space()) + .append(match enum_kind { + TypeEnumKind::Tagged(tag) => arena.text( + Color::White + .dimmed() + .paint(format!( + "[Tagged with {}]", + Color::LightGreen + .italic() + .dimmed() + .paint(format!("'{}'", tag)) + )) + .to_string(), + ), + TypeEnumKind::Untagged => { + arena.text(Color::White.dimmed().paint("[Untagged]").to_string()) + } + }) + .append(arena.hardline()) + .append(arena.intersperse( + variants.iter().map(|ev| { + let member_name = ev.name(); + let member_doc = ev.doc(); + let member_conf = ev.repr(); + arena.text("-").append(arena.space()).append({ + let mut doc = arena + .nil() + .append(match member_conf { + EnumVariantRepresentation::String(rep) => arena.text( + Color::Green + .bold() + .paint(&format!("{:?}", rep.to_lowercase())) + .to_string(), + ), + EnumVariantRepresentation::Wrapped(_) => arena + .text(Color::Green.bold().paint(member_name).to_string()), + }) + .append(": "); + + if let Some(member_doc) = member_doc { + doc = doc.append(render_markdown(member_doc)); + } + + doc.append( + Pretty::pretty( + match member_conf { + EnumVariantRepresentation::String(_) => { + arena.nil().into_doc() + } + + EnumVariantRepresentation::Wrapped(member_conf) => arena + .text(Color::LightRed.paint("Is a: ").to_string()) + .append(render_to_terminal(member_conf, arena)) + .into_doc(), + }, + arena, + ) + .nest(4), + ) + .nest(2) + }) + }), + Doc::hardline(), + )); + } + TypeKind::Array(conf) => { + doc = doc + .append(Color::LightRed.paint("Many of:").to_string()) + .append(arena.space()) + .append(render_to_terminal(conf, arena)); + } + TypeKind::HashMap { key, value } => { + doc = doc + .append(Color::LightRed.paint("Hashmap of").to_string()) + .append(arena.space()) + .append(key.name()) + .append(arena.space()) + .append(Color::LightRed.paint("to").to_string()) + .append(arena.space()) + .append(value.name()); + } + TypeKind::Wrapped(conf) => { + doc = doc.append(render_to_terminal(conf, arena)); + } + }; + + doc.into_doc() +} diff --git a/tests/complex_derive.rs b/tests/complex_derive.rs index 3f617ce..12ff27a 100644 --- a/tests/complex_derive.rs +++ b/tests/complex_derive.rs @@ -4,6 +4,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +use std::collections::HashMap; + use type_description::{AsTypeDescription, TypeDescription}; /// How to configure a kubernetes cluster @@ -25,6 +27,7 @@ pub struct KubeConfig { pub current_context: String, pub kind: String, pub users: Vec<User>, + pub values: HashMap<String, bool>, } #[derive(Debug, TypeDescription)] |