use std::cell::RefCell;
use self::thread_state::BumpAllocator;
use super::*;
pub struct PrintOptions {
pub max_width: u32,
pub indent_width: u8,
pub use_tabs: bool,
pub new_line_text: &'static str,
}
impl PrintOptions {
pub(super) fn to_printer_options(&self) -> PrinterOptions {
PrinterOptions {
indent_width: self.indent_width,
max_width: self.max_width,
#[cfg(feature = "tracing")]
enable_tracing: false,
}
}
}
pub fn format(get_print_items: impl FnOnce() -> PrintItems, options: PrintOptions) -> String {
increment_formatting_count();
let old_counts = thread_state::take_counts();
let print_items = get_print_items();
let result = thread_state::with_bump_allocator(|bump| {
let result = print_with_allocator(bump, &print_items, &options);
if decrement_formatting_count() {
bump.reset();
}
result
});
thread_state::set_counts(old_counts);
result
}
pub fn print(print_items: PrintItems, options: PrintOptions) -> String {
panic_if_not_formatting();
let old_counts = thread_state::take_counts();
let result = thread_state::with_bump_allocator(|bump| print_with_allocator(bump, &print_items, &options));
thread_state::set_counts(old_counts);
result
}
fn print_with_allocator(bump: &mut BumpAllocator, print_items: &PrintItems, options: &PrintOptions) -> String {
match Printer::new(bump, print_items.first_node, options.to_printer_options()).print() {
Some(write_items) => WriteItemsPrinter::from(options).print(write_items),
None => String::new(),
}
}
#[cfg(feature = "tracing")]
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TracingResult {
pub traces: Vec<Trace>,
pub writer_nodes: Vec<TraceWriterNode>,
pub print_nodes: Vec<TracePrintNode>,
}
#[cfg(feature = "tracing")]
pub fn trace_printing(get_print_items: impl FnOnce() -> PrintItems, options: PrintOptions) -> TracingResult {
use std::iter;
increment_formatting_count();
let print_items = get_print_items();
thread_state::with_bump_allocator(|bump| {
let tracing_result = Printer::new(bump, print_items.first_node, {
let mut printer_options = options.to_printer_options();
printer_options.enable_tracing = true;
printer_options
})
.print_for_tracing();
let writer_items_printer = WriteItemsPrinter::from(&options);
let result = TracingResult {
traces: tracing_result.traces,
writer_nodes: tracing_result
.writer_nodes
.into_iter()
.map(|node| {
let text = writer_items_printer.print(iter::once(node.item));
TraceWriterNode {
writer_node_id: node.graph_node_id,
previous_node_id: node.previous.map(|n| n.graph_node_id),
text,
}
})
.collect(),
print_nodes: super::get_trace_print_nodes(print_items.first_node),
};
if decrement_formatting_count() {
bump.reset();
}
result
})
}
thread_local! {
static FORMATTING_COUNT: RefCell<u32> = const { RefCell::new(0) };
}
fn increment_formatting_count() {
FORMATTING_COUNT.with(|formatting_count_cell| {
let mut formatting_count = formatting_count_cell.borrow_mut();
*formatting_count += 1;
})
}
fn decrement_formatting_count() -> bool {
FORMATTING_COUNT.with(|formatting_count_cell| {
let mut formatting_count = formatting_count_cell.borrow_mut();
*formatting_count -= 1;
*formatting_count == 0
})
}
fn panic_if_not_formatting() {
FORMATTING_COUNT.with(|formatting_count_cell| {
if *formatting_count_cell.borrow() == 0 {
panic!("dprint_core::formatting::print cannot be called except within the provided closure to dprint_core::formatting::format");
}
})
}
#[cfg(test)]
mod test {
use crate::formatting::LineNumber;
use super::super::PrintItems;
use super::format;
use super::PrintOptions;
#[test]
fn test_format_in_format() {
assert_eq!(
format(
|| {
let mut items = PrintItems::new();
assert_eq!(LineNumber::new("").unique_id(), 0);
assert_eq!(LineNumber::new("").unique_id(), 1);
assert_eq!(LineNumber::new("").unique_id(), 2);
items.push_str_runtime_width_computed("test");
items.push_string(format(
|| {
assert_eq!(LineNumber::new("").unique_id(), 0);
assert_eq!(LineNumber::new("").unique_id(), 1);
"test".into()
},
get_print_options(),
));
assert_eq!(LineNumber::new("").unique_id(), 3);
items
},
get_print_options(),
),
"testtest"
);
}
fn get_print_options() -> PrintOptions {
PrintOptions {
max_width: 40,
indent_width: 2,
use_tabs: false,
new_line_text: "\n",
}
}
}