1use std::{marker::PhantomData, ptr::NonNull};
7
8use crate::{
9 alloc::{Allocator, Bytes, Object},
10 error::{Error, Result, from_result},
11 ffi,
12 terminal::Terminal,
13};
14
15#[derive(Debug)]
17pub struct Formatter<'t, 'alloc: 'cb, 'cb: 't> {
18 inner: Object<'alloc, ffi::GhosttyFormatter>,
19 _terminal: PhantomData<&'t Terminal<'alloc, 'cb>>,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub struct FormatterOptions {
25 pub format: Format,
27 pub trim: bool,
29 pub unwrap: bool,
31}
32
33impl<'t, 'alloc: 'cb, 'cb: 't> Formatter<'t, 'alloc, 'cb> {
34 pub fn new(terminal: &'t Terminal<'alloc, 'cb>, opts: FormatterOptions) -> Result<Self> {
36 unsafe { Self::new_inner(std::ptr::null(), terminal, opts) }
38 }
39
40 pub fn new_with_alloc<'ctx: 'alloc, Ctx>(
45 alloc: &'alloc Allocator<'ctx, Ctx>,
46 terminal: &'t Terminal<'alloc, 'cb>,
47 opts: FormatterOptions,
48 ) -> Result<Self> {
49 unsafe { Self::new_inner(alloc.to_raw(), terminal, opts) }
51 }
52
53 unsafe fn new_inner(
54 alloc: *const ffi::GhosttyAllocator,
55 terminal: &'t Terminal<'alloc, 'cb>,
56 opts: FormatterOptions,
57 ) -> Result<Self> {
58 let mut raw: ffi::GhosttyFormatter_ptr = std::ptr::null_mut();
59 let result = unsafe {
60 ffi::ghostty_formatter_terminal_new(
61 alloc,
62 &raw mut raw,
63 terminal.inner.as_raw(),
64 opts.into(),
65 )
66 };
67 from_result(result)?;
68
69 Ok(Self {
70 inner: Object::new(raw)?,
71 _terminal: PhantomData,
72 })
73 }
74
75 pub fn format_alloc<'a, 'ctx: 'a, Ctx>(
80 &mut self,
81 alloc: Option<&'a Allocator<'ctx, Ctx>>,
82 ) -> Result<Bytes<'a>> {
83 let alloc = if let Some(alloc) = alloc {
84 alloc.to_raw()
85 } else {
86 std::ptr::null()
87 };
88
89 let mut bytes = std::ptr::null_mut();
90 let mut len = 0usize;
91 let result = unsafe {
92 ffi::ghostty_formatter_format_alloc(
93 self.inner.as_raw(),
94 alloc,
95 std::ptr::from_mut(&mut bytes),
96 std::ptr::from_mut(&mut len),
97 )
98 };
99 from_result(result)?;
100
101 let ptr = NonNull::new(bytes).ok_or(Error::OutOfMemory)?;
102 Ok(unsafe { Bytes::from_raw_parts(ptr, len, alloc) })
103 }
104
105 pub fn format_buf(&mut self, buf: &mut [u8]) -> Result<usize> {
111 let mut len = 0usize;
112 let result = unsafe {
113 ffi::ghostty_formatter_format_buf(
114 self.inner.as_raw(),
115 std::ptr::from_mut(buf).cast(),
116 buf.len(),
117 std::ptr::from_mut(&mut len),
118 )
119 };
120 from_result(result)?;
121 Ok(len)
122 }
123
124 pub fn format_len(&mut self) -> Result<usize> {
129 let mut len = 0usize;
130 let result = unsafe {
131 ffi::ghostty_formatter_format_buf(
132 self.inner.as_raw(),
133 std::ptr::null_mut(),
134 0,
135 std::ptr::from_mut(&mut len),
136 )
137 };
138 match from_result(result) {
140 Err(Error::OutOfSpace { .. }) => Ok(len),
141 Err(e) => Err(e),
142 Ok(()) => Err(Error::InvalidValue),
143 }
144 }
145}
146
147impl Drop for Formatter<'_, '_, '_> {
148 fn drop(&mut self) {
149 unsafe { ffi::ghostty_formatter_free(self.inner.as_raw()) }
150 }
151}
152
153impl From<FormatterOptions> for ffi::GhosttyFormatterTerminalOptions {
154 fn from(value: FormatterOptions) -> Self {
155 Self {
156 size: std::mem::size_of::<ffi::GhosttyFormatterTerminalOptions>(),
157 emit: value.format.into(),
158 trim: value.trim,
159 extra: ffi::GhosttyFormatterTerminalExtra::default(),
160 unwrap: value.unwrap,
161 }
162 }
163}
164
165#[repr(u32)]
167#[derive(Debug, Clone, Copy, PartialEq, Eq, int_enum::IntEnum)]
168pub enum Format {
169 Plain = ffi::GhosttyFormatterFormat_GHOSTTY_FORMATTER_FORMAT_PLAIN,
171 Vt = ffi::GhosttyFormatterFormat_GHOSTTY_FORMATTER_FORMAT_VT,
173 Html = ffi::GhosttyFormatterFormat_GHOSTTY_FORMATTER_FORMAT_HTML,
175}