1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
use indenter::{indented, Indented};
use std::fmt::{Display, Formatter, Result};
/// Print source code in a block.
///
/// This function accepts a writing function, and returns a [`Block`]. When the `Block`
/// is printed via the `fmt::Display` trait, it writes an open brace and new line,
/// indents the formatter, and calls the function to write the contents of the block.
/// Then, it unindents the formatter and writes a close brace.
///
/// This allows for generating source code without having to manually insert
/// opening/closing braces for blocks or worry about indentation.
///
/// # Examples
///
/// ```ignore
/// writeln!(
/// f,
/// "if (ok) {if_true} else {if_false}"
/// if_true = block(|mut f| {
/// writeln!(f, "console.log(\"ok\");")
/// }),
/// if_false = block(|mut f| {
/// writeln!(f, "console.log(\"err\");")
/// })
/// )?;
/// ```
/// This writes:
/// ```js
/// if (ok) {
/// console.log("ok");
/// } else {
/// console.log("err");
/// }
/// ```
pub fn block<F>(f: F) -> Block<F>
where
F: Fn(Indented<Formatter>) -> Result,
{
Block(f)
}
/// Generate source code in an indented block.
///
/// See [`block`] for more info.
pub struct Block<F>(F)
where
F: Fn(Indented<Formatter>) -> Result;
impl<F> Display for Block<F>
where
F: Fn(Indented<Formatter>) -> Result,
{
fn fmt(&self, f: &mut Formatter) -> Result {
writeln!(f, "{{")?;
self.0(indented(f).with_str(" "))?;
write!(f, "}}")
}
}
/// Write arbitrarily complex statements inline.
///
/// When generating source code, sometimes it's difficult to see where parantheses
/// balance or remember to add a semicolon at the end of a long expression. This
/// function allows you to wrap the display logic of a complex expression into
/// a single type, making it easier to format the code around the expression.
///
/// # Examples
///
/// Variable assignment without forgetting the semicolon at the end.
/// ```ignore
/// writeln!(f, "const out = {};", expr(|mut f| {
/// write!(f, "1 + 2")
/// }))?;
/// ```
/// This writes:
/// ```js
/// const out = 1 + 2;
/// ```
pub fn expr<F>(f: F) -> Expr<F>
where
F: Fn(&mut Formatter) -> Result,
{
Expr(f)
}
/// Generate source code as an expression.
///
/// See [`expr`] for more info.
pub struct Expr<F>(F)
where
F: Fn(&mut Formatter) -> Result;
impl<F> Display for Expr<F>
where
F: Fn(&mut Formatter) -> Result,
{
fn fmt(&self, f: &mut Formatter) -> Result {
self.0(f)
}
}
/// Write an immediately invoked function expression (IIFE).
///
/// This function accepts a closure that writes the contents of the IIFE,
/// and generates the proper wrapping and indentation.
///
/// # Examples
///
/// ```ignore
/// writeln!(f, "const out = {};", iife(|mut f| {
/// writeln!(f, "const out = {{}};")?;
/// writeln!(f, "out.a = 7;")?;
/// writeln!(f, "return out;")?;
/// }))?;
/// ```
/// This generates
/// ```js
/// const out = (() => {
/// const out = {};
/// out.a = 7;
/// return out;
/// })();
/// ```
pub fn iife<F>(f: F) -> IIFE<F>
where
F: Fn(Indented<Formatter>) -> Result,
{
IIFE(f)
}
/// An `fmt::Display` type returned by [`iife`].
pub struct IIFE<F>(F)
where
F: Fn(Indented<Formatter>) -> Result;
impl<F> Display for IIFE<F>
where
F: Fn(Indented<Formatter>) -> Result,
{
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "(() => {})()", block(&self.0))
}
}
pub fn ts_doc<F>(f: F) -> TsDoc<F>
where
F: Fn(Indented<Formatter>) -> Result,
{
TsDoc(f)
}
/// An `fmt::Display` type returned by [`ts_doc`].
pub struct TsDoc<F>(F)
where
F: Fn(Indented<Formatter>) -> Result;
impl<F> Display for TsDoc<F>
where
F: Fn(Indented<Formatter>) -> Result,
{
fn fmt(&self, f: &mut Formatter) -> Result {
writeln!(f, "/**")?;
self.0(indented(f).with_str(" * "))?;
writeln!(f, " */")
}
}