use crate::{__ansi_consts, Ansi, Display, FmtResult, Formatter, is, whilst};
impl Ansi {
pub const fn title_all(text: &str) -> AnsiOscText<'_> {
AnsiOsc::text("0", text)
}
pub const fn title_icon(text: &str) -> AnsiOscText<'_> {
AnsiOsc::text("1", text)
}
pub const fn title_window(text: &str) -> AnsiOscText<'_> {
AnsiOsc::text("2", text)
}
pub const fn link<'a>(text: &'a str, uri: &'a str) -> AnsiLink<'a> {
AnsiLink::new(text, uri)
}
pub const fn link_with_id<'a>(text: &'a str, uri: &'a str, id: &'a str) -> AnsiLink<'a> {
AnsiLink::with_id(text, uri, id)
}
__ansi_consts! {
pub const HYPERLINK_CLOSE: [u8;7] = "\x1b]8;;\x1b\\", *b"\x1b]8;;\x1b\\";
}
pub const fn clipboard_base64<'a>(selection: &'a str, base64: &'a str) -> AnsiOsc<'a, 2> {
AnsiOsc::new("52", [selection, base64])
}
pub const fn clipboard_query_clipboard() -> AnsiOsc<'static, 2> {
AnsiOsc::new("52", ["c", "?"])
}
pub const fn clipboard_query<'a>(selection: &'a str) -> AnsiOsc<'a, 2> {
AnsiOsc::new("52", [selection, "?"])
}
}
#[doc = crate::_tags!(term string)]
#[doc = crate::_doc_meta!{
location("sys/os/term"),
#[cfg(target_pointer_width = "32")]
test_size_of(AnsiOsc1: AnsiOsc<'_, 1> = 16|128),
#[cfg(target_pointer_width = "64")]
test_size_of(AnsiOsc1: AnsiOsc<'_, 1> = 32|256),
}]
#[must_use]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AnsiOsc<'a, const N: usize> {
pub command: &'static str,
pub fields: [&'a str; N],
}
impl<'a, const N: usize> AnsiOsc<'a, N> {
pub const fn new(command: &'static str, fields: [&'a str; N]) -> Self {
Self { command, fields }
}
#[must_use]
pub const fn len(self) -> usize {
let mut len = 2 + self.command.len() + 2; whilst! { i in 0..N; {
len += 1 + self.fields[i].len(); }}
len
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.len() == 0
}
pub const fn write_to(self, dst: &mut [u8]) -> Result<usize, usize> {
let needed = self.len();
let mut offset = 0;
_put!(dst, offset, needed, b"\x1b]");
_put!(dst, offset, needed, self.command.as_bytes());
whilst! { i in 0..N; {
_put!(dst, offset, needed, b";");
_put!(dst, offset, needed, self.fields[i].as_bytes());
}}
_put!(dst, offset, needed, b"\x1b\\");
Ok(offset)
}
fn fmt_streamed(self, f: &mut Formatter<'_>) -> FmtResult<()> {
f.write_str("\x1b]")?;
f.write_str(self.command)?;
whilst! { i in 0..N; {
f.write_str(";")?;
f.write_str(self.fields[i])?;
}}
f.write_str("\x1b\\")
}
}
impl<const N: usize> Display for AnsiOsc<'_, N> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult<()> {
self.fmt_streamed(f)
}
}
type AnsiOscText<'a> = AnsiOsc<'a, 1>;
impl<'a> AnsiOsc<'a, 1> {
pub const fn text(command: &'static str, text: &'a str) -> Self {
Self::new(command, [text])
}
}
#[doc = crate::_tags!(term web string)]
#[doc = crate::_doc_meta!{
location("sys/os/term"),
#[cfg(target_pointer_width = "32")]
test_size_of(AnsiLink: AnsiLink<'_> = 24|192),
#[cfg(target_pointer_width = "64")]
test_size_of(AnsiLink: AnsiLink<'_> = 48|384),
}]
#[must_use]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct AnsiLink<'a> {
pub text: &'a str,
pub uri: &'a str,
pub id: Option<&'a str>,
}
impl<'a> AnsiLink<'a> {
pub const fn new(text: &'a str, uri: &'a str) -> Self {
Self { text, uri, id: None }
}
pub const fn with_id(text: &'a str, uri: &'a str, id: &'a str) -> Self {
Self { text, uri, id: Some(id) }
}
#[must_use]
pub const fn len(self) -> usize {
let id_len = match self.id {
Some(id) => 3 + id.len(), None => 0,
};
4 + id_len + 1 + self.uri.len() + 2 + self.text.len() + Ansi::HYPERLINK_CLOSE_B.len()
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.len() == 0
}
pub const fn write_to(self, dst: &mut [u8]) -> Result<usize, usize> {
let needed = self.len();
let mut offset = 0;
_put!(dst, offset, needed, b"\x1b]8;");
if let Some(id) = self.id {
_put!(dst, offset, needed, b"id=");
_put!(dst, offset, needed, id.as_bytes());
}
_put!(dst, offset, needed, b";");
_put!(dst, offset, needed, self.uri.as_bytes());
_put!(dst, offset, needed, b"\x1b\\");
_put!(dst, offset, needed, self.text.as_bytes());
_put!(dst, offset, needed, &Ansi::HYPERLINK_CLOSE_B);
Ok(offset)
}
fn fmt_streamed(self, f: &mut Formatter<'_>) -> FmtResult<()> {
f.write_str("\x1b]8;")?;
if let Some(id) = self.id {
f.write_str("id=")?;
f.write_str(id)?;
}
f.write_str(";")?;
f.write_str(self.uri)?;
f.write_str("\x1b\\")?;
f.write_str(self.text)?;
f.write_str(Ansi::HYPERLINK_CLOSE)
}
}
#[rustfmt::skip]
impl Display for AnsiLink<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult<()> { self.fmt_streamed(f) }
}
#[inline(always)] #[rustfmt::skip]
const fn _put_bytes(dst: &mut [u8], offset: usize, src: &[u8], needed: usize)
-> Result<usize, usize> {
is! { offset > dst.len() || src.len() > dst.len() - offset, return Err(needed) }
whilst! { i in 0..src.len(); { dst[offset + i] = src[i]; }}
Ok(offset + src.len())
}
macro_rules! _put {
($dst:expr, $offset:ident, $needed:expr, $src:expr) => {
match _put_bytes($dst, $offset, $src, $needed) {
Ok(new_offset) => $offset = new_offset,
Err(needed) => return Err(needed),
}
};
}
use _put;