mirror of
https://github.com/LordMZTE/mcstat.git
synced 2024-05-05 22:21:11 +02:00
server description now printed with coloring
This commit is contained in:
parent
258899cf61
commit
f015c85c60
14
Cargo.toml
14
Cargo.toml
|
@ -7,25 +7,25 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.40"
|
anyhow = "1.0.43"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
clap = "2.33.3"
|
crossterm = "0.21.0"
|
||||||
image = "0.23.14"
|
image = "0.23.14"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.1"
|
||||||
serde_json = "1.0.64"
|
serde_json = "1.0.67"
|
||||||
smart-default = "0.6.0"
|
smart-default = "0.6.0"
|
||||||
structopt = "0.3.21"
|
structopt = "0.3.23"
|
||||||
term_size = "0.3.2"
|
term_size = "0.3.2"
|
||||||
termcolor = "1.1.2"
|
termcolor = "1.1.2"
|
||||||
unicode-width = "0.1.8"
|
unicode-width = "0.1.8"
|
||||||
viuer = "0.4.0"
|
viuer = "0.5.2"
|
||||||
|
|
||||||
[dependencies.async-minecraft-ping]
|
[dependencies.async-minecraft-ping]
|
||||||
git = "https://github.com/LordMZTE/async-minecraft-ping.git"
|
git = "https://github.com/LordMZTE/async-minecraft-ping.git"
|
||||||
tag = "v0.2.5"
|
tag = "v0.2.5"
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
version = "1.5.0"
|
version = "1.11.0"
|
||||||
features = ["rt-multi-thread", "macros", "time"]
|
features = ["rt-multi-thread", "macros", "time"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
97
src/lib.rs
97
src/lib.rs
|
@ -3,9 +3,13 @@ extern crate smart_default;
|
||||||
|
|
||||||
use crate::output::Table;
|
use crate::output::Table;
|
||||||
use anyhow::{anyhow, bail, Context};
|
use anyhow::{anyhow, bail, Context};
|
||||||
|
use crossterm::{
|
||||||
|
style::{Attribute, Color, Print, ResetColor, SetAttribute, SetForegroundColor},
|
||||||
|
ExecutableCommand,
|
||||||
|
};
|
||||||
use image::{DynamicImage, ImageFormat};
|
use image::{DynamicImage, ImageFormat};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::io::Cursor;
|
use std::io::{self, Cursor, Write};
|
||||||
|
|
||||||
pub mod output;
|
pub mod output;
|
||||||
|
|
||||||
|
@ -26,18 +30,85 @@ macro_rules! none_if_empty {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_formatting(s: &str) -> String {
|
/// Print mincraft-formatted text to `out` using crossterm
|
||||||
let chars = s.char_indices().rev();
|
pub fn print_mc_formatted(s: &str, mut out: impl Write) -> io::Result<()> {
|
||||||
let mut buf = s.to_owned();
|
macro_rules! exec {
|
||||||
for c in chars {
|
(fg, $color:ident) => {
|
||||||
if c.1 == '§' {
|
exec!(SetForegroundColor(Color::$color))
|
||||||
buf.remove(c.0);
|
};
|
||||||
if c.0 < buf.len() {
|
|
||||||
buf.remove(c.0);
|
(at, $attr:ident) => {
|
||||||
|
exec!(SetAttribute(Attribute::$attr))
|
||||||
|
};
|
||||||
|
|
||||||
|
($action:expr) => {{
|
||||||
|
out.execute($action)?;
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut splits = s.split('§');
|
||||||
|
if let Some(n) = splits.next() {
|
||||||
|
exec!(Print(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut empty = true;
|
||||||
|
for split in splits {
|
||||||
|
empty = false;
|
||||||
|
if let Some(c) = split.chars().next() {
|
||||||
|
match c {
|
||||||
|
// Colors
|
||||||
|
'0' => exec!(fg, Black),
|
||||||
|
'1' => exec!(fg, DarkBlue),
|
||||||
|
'2' => exec!(fg, DarkGreen),
|
||||||
|
'3' => exec!(fg, DarkCyan),
|
||||||
|
'4' => exec!(fg, DarkRed),
|
||||||
|
'5' => exec!(fg, DarkMagenta),
|
||||||
|
'6' => exec!(fg, DarkYellow),
|
||||||
|
'7' => exec!(fg, Grey),
|
||||||
|
'8' => exec!(fg, DarkGrey),
|
||||||
|
'9' => exec!(fg, Blue),
|
||||||
|
'a' => exec!(fg, Green),
|
||||||
|
'b' => exec!(fg, Cyan),
|
||||||
|
'c' => exec!(fg, Red),
|
||||||
|
'd' => exec!(fg, Magenta),
|
||||||
|
'e' => exec!(fg, Yellow),
|
||||||
|
'f' => exec!(fg, White),
|
||||||
|
|
||||||
|
// Formatting
|
||||||
|
// Obfuscated. This is the closest thing, althogh not many terminals support it.
|
||||||
|
'k' => exec!(at, RapidBlink),
|
||||||
|
'l' => exec!(at, Bold),
|
||||||
|
'm' => exec!(at, CrossedOut),
|
||||||
|
'n' => exec!(at, Underlined),
|
||||||
|
'o' => exec!(at, Italic),
|
||||||
|
'r' => exec!(ResetColor),
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
|
exec!(Print(&split[1..]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf
|
|
||||||
|
// no need to reset color if there were no escape codes.
|
||||||
|
if !empty {
|
||||||
|
exec!(ResetColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mc_formatted_to_ansi(s: &str) -> io::Result<String> {
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
let mut c = Cursor::new(&mut bytes);
|
||||||
|
print_mc_formatted(s, &mut c)?;
|
||||||
|
|
||||||
|
// this shouldn't be able to fail, as we started of with a valid utf8 string.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
let out = String::from_utf8(bytes).unwrap();
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
let out = unsafe { String::from_utf8_unchecked(bytes) };
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// formats a iterator to a readable list
|
/// formats a iterator to a readable list
|
||||||
|
@ -65,13 +136,13 @@ pub fn get_table<'a>(
|
||||||
/// parses a base64 formatted image
|
/// parses a base64 formatted image
|
||||||
pub fn parse_base64_image(data: String) -> anyhow::Result<DynamicImage> {
|
pub fn parse_base64_image(data: String) -> anyhow::Result<DynamicImage> {
|
||||||
let (header, data) = data
|
let (header, data) = data
|
||||||
.split_once(",")
|
.split_once(',')
|
||||||
.context("Couldn't parse base64 image due to missing format header.")?;
|
.context("Couldn't parse base64 image due to missing format header.")?;
|
||||||
let (data_type, image_format) = header
|
let (data_type, image_format) = header
|
||||||
.split_once("/")
|
.split_once('/')
|
||||||
.context("Failed to parse base64 image, header has invalid format.")?;
|
.context("Failed to parse base64 image, header has invalid format.")?;
|
||||||
let image_format = image_format
|
let image_format = image_format
|
||||||
.split(";")
|
.split(';')
|
||||||
.next()
|
.next()
|
||||||
.context("Failed to parse base64 image, header has invalid format.")?;
|
.context("Failed to parse base64 image, header has invalid format.")?;
|
||||||
|
|
||||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -6,7 +6,7 @@ use structopt::StructOpt;
|
||||||
use time::{Duration, Instant};
|
use time::{Duration, Instant};
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
|
|
||||||
use mcstat::{get_table, none_if_empty, output::Table, parse_base64_image, remove_formatting};
|
use mcstat::{get_table, mc_formatted_to_ansi, none_if_empty, output::Table, parse_base64_image};
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
#[structopt(
|
#[structopt(
|
||||||
|
@ -197,25 +197,29 @@ fn format_table(
|
||||||
table.max_block_width = w;
|
table.max_block_width = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(s) = none_if_empty!(remove_formatting(&response.description.get_text())) {
|
if let Some(s) = none_if_empty!(mc_formatted_to_ansi(response.description.get_text())
|
||||||
|
.unwrap_or_else(|e| format!("Error: {}", e)))
|
||||||
|
{
|
||||||
table.big_entry("Description", s);
|
table.big_entry("Description", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ServerDescription::Big(big_desc) = &response.description {
|
if let ServerDescription::Big(big_desc) = &response.description {
|
||||||
let desc = &big_desc.extra;
|
let desc = &big_desc.extra;
|
||||||
let txt = desc.into_iter().map(|p| p.text.clone()).collect::<String>();
|
let txt = desc.iter().map(|p| p.text.clone()).collect::<String>();
|
||||||
if let Some(s) = none_if_empty!(txt) {
|
if let Some(s) = none_if_empty!(txt) {
|
||||||
table.big_entry("Extra Description", s);
|
table.big_entry("Extra Description", s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(s) = none_if_empty!(remove_formatting(&player_sample)) {
|
if let Some(s) = none_if_empty!(
|
||||||
|
mc_formatted_to_ansi(&player_sample).unwrap_or_else(|e| format!("Error: {}", e))
|
||||||
|
) {
|
||||||
table.big_entry("Player Sample", s);
|
table.big_entry("Player Sample", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
table.blank();
|
table.blank();
|
||||||
|
|
||||||
if let Some(s) = none_if_empty!(remove_formatting(&response.version.name)) {
|
if let Some(s) = none_if_empty!(&response.version.name) {
|
||||||
table.small_entry("Server Version", s);
|
table.small_entry("Server Version", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue