use crate::{schemas::AddressType, soutln};
use std::env;
use std::io::{IsTerminal, Write};
use std::os::fd::AsRawFd;
use std::process::{Command, Stdio};
fn red_text(text: &str) -> String {
format!("\x1b[1;31m{text}\x1b[0m")
}
fn cyan_text(text: &str) -> String {
format!("\x1B[36m{text}\x1B[0m")
}
fn yellow_text(text: &str) -> String {
format!("\x1b[1;33m{text}\x1b[0m")
}
fn bold_text(text: &str) -> String {
format!("\x1B[1m{text}\x1B[0m")
}
pub fn format_known_address(remote_address: &str, address_type: &AddressType) -> String {
match address_type {
AddressType::Unspecified => {
format!("*{remote_address}*")
}
AddressType::Localhost => {
format!("*{remote_address} localhost*")
}
AddressType::Extern => remote_address.to_string(),
}
}
pub fn render_info_line(text: &str) -> String {
bold_text(&format!("{} {}", cyan_text("Info:"), text))
}
pub fn pretty_print_info(text: &str) {
soutln!("{}", render_info_line(text));
}
pub fn pretty_print_error(text: &str) {
soutln!("{}", bold_text(&format!("{} {}", red_text("Error:"), text)));
}
pub fn pretty_print_warning(text: &str) {
soutln!(
"{}",
bold_text(&format!("{} {}", yellow_text("Warning:"), text))
);
}
pub fn pretty_print_syntax_error(preamble: &str, text: &str, line: usize, column: usize) {
let erronous_line: &str = text.lines().nth(line - 1).unwrap_or(text);
let line_pointer = "└─>";
pretty_print_error(preamble);
soutln!(" {}", red_text("│"));
soutln!(" {} {}", red_text(line_pointer), erronous_line);
soutln!(
" {} {}",
" ".repeat(line_pointer.chars().count() + column - 1),
red_text("^")
);
}
pub fn is_stdout_tty() -> bool {
std::io::stdout().is_terminal()
}
pub fn terminal_rows() -> Option<usize> {
if !is_stdout_tty() {
return None;
}
unsafe {
let fd = std::io::stdout().as_raw_fd();
let mut ws: libc::winsize = std::mem::zeroed();
if libc::ioctl(fd, libc::TIOCGWINSZ, &mut ws) == 0 && ws.ws_row > 0 {
return Some(ws.ws_row as usize);
}
}
None
}
fn write_to_pager(text: &str) -> std::io::Result<()> {
let pager_env = env::var("SOMO_PAGER").or_else(|_| env::var("PAGER")).ok();
let mut parts: Vec<String> = match pager_env {
Some(p) if !p.trim().is_empty() => {
p.split_whitespace().map(|s| s.to_string()).collect()
}
_ => vec!["less".into(), "-R".into()],
};
let program = parts.remove(0);
let mut child = Command::new(program)
.args(parts)
.stdin(Stdio::piped())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()?;
if let Some(mut stdin) = child.stdin.take() {
let _ = stdin.write_all(text.as_bytes());
}
let _ = child.wait();
Ok(())
}
pub fn page_or_print(text: &str, no_paging: bool) {
let should_page = !no_paging
&& terminal_rows()
.map(|rows| text.lines().count() >= rows)
.unwrap_or(false);
if should_page && write_to_pager(text).is_ok() {
return;
}
soutln!("{}", text);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_known_address_localhost() {
let addr = "127.0.0.1".to_string();
let result = format_known_address(&addr, &AddressType::Localhost);
assert_eq!(result, "*127.0.0.1 localhost*");
}
#[test]
fn test_format_known_address_unspecified() {
let addr = "0.0.0.0".to_string();
let result = format_known_address(&addr, &AddressType::Unspecified);
assert_eq!(result, "*0.0.0.0*");
}
#[test]
fn test_format_known_address_extern() {
let addr = "123.123.123".to_string();
let result = format_known_address(&addr, &AddressType::Extern);
assert_eq!(result, "123.123.123");
}
}