1use std::net::{IpAddr, Ipv4Addr};
4use std::{iter, str::from_utf8};
5
6use crate::data::{Palette, PaletteColor, PaletteSource, ThemeHue};
7use crate::envs::get_session_name;
8use crate::input::options::Options;
9use colorsys::{Ansi256, Rgb};
10use strip_ansi_escapes::strip;
11use unicode_width::UnicodeWidthStr;
12
13#[cfg(unix)]
14pub use unix_only::*;
15
16#[cfg(unix)]
17mod unix_only {
18 use std::os::unix::fs::PermissionsExt;
19 use std::path::Path;
20 use std::{fs, io};
21
22 pub fn set_permissions(path: &Path, mode: u32) -> io::Result<()> {
23 let mut permissions = fs::metadata(path)?.permissions();
24 permissions.set_mode(mode);
25 fs::set_permissions(path, permissions)
26 }
27}
28
29#[cfg(not(unix))]
30pub fn set_permissions(_path: &std::path::Path, _mode: u32) -> std::io::Result<()> {
31 Ok(())
32}
33
34pub fn ansi_len(s: &str) -> usize {
35 from_utf8(&strip(s).unwrap()).unwrap().width()
36}
37
38pub fn clean_string_from_control_and_linebreak(input: &str) -> String {
39 input
40 .chars()
41 .filter(|c| {
42 !c.is_control() &&
43 *c != '\n' && *c != '\r' && *c != '\u{2028}' && *c != '\u{2029}' })
48 .collect()
49}
50
51pub fn adjust_to_size(s: &str, rows: usize, columns: usize) -> String {
52 s.lines()
53 .map(|l| {
54 let actual_len = ansi_len(l);
55 if actual_len > columns {
56 let mut line = String::from(l);
57 line.truncate(columns);
58 line
59 } else {
60 [l, &str::repeat(" ", columns - ansi_len(l))].concat()
61 }
62 })
63 .chain(iter::repeat(str::repeat(" ", columns)))
64 .take(rows)
65 .collect::<Vec<_>>()
66 .join("\n\r")
67}
68
69pub fn make_terminal_title(pane_title: &str) -> String {
70 format!(
71 "\u{1b}]0;{}{}\u{07}",
72 get_session_name()
73 .map(|n| if pane_title.is_empty() {
74 format!("{}", n)
75 } else {
76 format!("{} | ", n)
77 })
78 .unwrap_or_default(),
79 pane_title
80 )
81}
82
83pub mod colors {
85 pub const WHITE: u8 = 255;
86 pub const GREEN: u8 = 154;
87 pub const GRAY: u8 = 238;
88 pub const BRIGHT_GRAY: u8 = 245;
89 pub const RED: u8 = 124;
90 pub const ORANGE: u8 = 166;
91 pub const BLACK: u8 = 16;
92 pub const MAGENTA: u8 = 201;
93 pub const CYAN: u8 = 51;
94 pub const YELLOW: u8 = 226;
95 pub const BLUE: u8 = 45;
96 pub const PURPLE: u8 = 99;
97 pub const GOLD: u8 = 136;
98 pub const SILVER: u8 = 245;
99 pub const PINK: u8 = 207;
100 pub const BROWN: u8 = 215;
101}
102
103pub fn _hex_to_rgb(hex: &str) -> (u8, u8, u8) {
104 Rgb::from_hex_str(hex)
105 .expect("The passed argument must be a valid hex color")
106 .into()
107}
108
109pub fn eightbit_to_rgb(c: u8) -> (u8, u8, u8) {
110 Ansi256::new(c).as_rgb().into()
111}
112
113pub fn default_palette() -> Palette {
114 Palette {
115 source: PaletteSource::Default,
116 theme_hue: ThemeHue::Dark,
117 fg: PaletteColor::EightBit(colors::BRIGHT_GRAY),
118 bg: PaletteColor::EightBit(colors::GRAY),
119 black: PaletteColor::EightBit(colors::BLACK),
120 red: PaletteColor::EightBit(colors::RED),
121 green: PaletteColor::EightBit(colors::GREEN),
122 yellow: PaletteColor::EightBit(colors::YELLOW),
123 blue: PaletteColor::EightBit(colors::BLUE),
124 magenta: PaletteColor::EightBit(colors::MAGENTA),
125 cyan: PaletteColor::EightBit(colors::CYAN),
126 white: PaletteColor::EightBit(colors::WHITE),
127 orange: PaletteColor::EightBit(colors::ORANGE),
128 gray: PaletteColor::EightBit(colors::GRAY),
129 purple: PaletteColor::EightBit(colors::PURPLE),
130 gold: PaletteColor::EightBit(colors::GOLD),
131 silver: PaletteColor::EightBit(colors::SILVER),
132 pink: PaletteColor::EightBit(colors::PINK),
133 brown: PaletteColor::EightBit(colors::BROWN),
134 }
135}
136
137pub fn detect_theme_hue(bg: PaletteColor) -> ThemeHue {
139 match bg {
140 PaletteColor::Rgb((r, g, b)) => {
141 let hsp: f64 = (0.299 * (r as f64 * r as f64)
143 + 0.587 * (g as f64 * g as f64)
144 + 0.114 * (b as f64 * b as f64))
145 .sqrt();
146 match hsp > 127.5 {
147 true => ThemeHue::Light,
148 false => ThemeHue::Dark,
149 }
150 },
151 _ => ThemeHue::Dark,
152 }
153}
154
155pub fn version_number(mut version: &str) -> usize {
161 if let Some(separator) = version.rfind('-') {
162 version = &version[..separator];
163 }
164
165 let mut version_number = 0;
166
167 let semver_versions = version.split('.');
168 for (i, semver_version) in semver_versions.rev().enumerate() {
169 let semver_number = semver_version.parse::<usize>().unwrap_or(0);
170 version_number += usize::pow(100, i as u32) * semver_number;
171 }
172
173 version_number
174}
175
176pub fn web_server_base_url(
177 web_server_ip: IpAddr,
178 web_server_port: u16,
179 has_certificate: bool,
180 enforce_https_for_localhost: bool,
181) -> String {
182 let is_loopback = match web_server_ip {
183 IpAddr::V4(ipv4) => ipv4.is_loopback(),
184 IpAddr::V6(ipv6) => ipv6.is_loopback(),
185 };
186
187 let url_prefix = if is_loopback && !enforce_https_for_localhost && !has_certificate {
188 "http"
189 } else {
190 "https"
191 };
192 format!("{}://{}:{}", url_prefix, web_server_ip, web_server_port)
193}
194
195pub fn web_server_base_url_from_config(config_options: Options) -> String {
196 let web_server_ip = config_options
197 .web_server_ip
198 .unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
199 let web_server_port = config_options.web_server_port.unwrap_or_else(|| 8082);
200 let has_certificate =
201 config_options.web_server_cert.is_some() && config_options.web_server_key.is_some();
202 let enforce_https_for_localhost = config_options.enforce_https_for_localhost.unwrap_or(false);
203 web_server_base_url(
204 web_server_ip,
205 web_server_port,
206 has_certificate,
207 enforce_https_for_localhost,
208 )
209}