1e73685ebSopenharmony_ci//! Renders the preview SVG for the README. 2e73685ebSopenharmony_ci//! 3e73685ebSopenharmony_ci//! To update the preview, execute the following command from the top level of 4e73685ebSopenharmony_ci//! the repository: 5e73685ebSopenharmony_ci//! 6e73685ebSopenharmony_ci//! ```sh 7e73685ebSopenharmony_ci//! cargo run --example readme_preview svg > codespan-reporting/assets/readme_preview.svg 8e73685ebSopenharmony_ci//! ``` 9e73685ebSopenharmony_ci 10e73685ebSopenharmony_ciuse codespan_reporting::diagnostic::{Diagnostic, Label}; 11e73685ebSopenharmony_ciuse codespan_reporting::files::SimpleFile; 12e73685ebSopenharmony_ciuse codespan_reporting::term::termcolor::{Color, ColorSpec, StandardStream, WriteColor}; 13e73685ebSopenharmony_ciuse codespan_reporting::term::{self, ColorArg}; 14e73685ebSopenharmony_ciuse std::io::{self, Write}; 15e73685ebSopenharmony_ciuse structopt::StructOpt; 16e73685ebSopenharmony_ci 17e73685ebSopenharmony_ci#[derive(Debug, StructOpt)] 18e73685ebSopenharmony_ci#[structopt(name = "emit")] 19e73685ebSopenharmony_cipub enum Opts { 20e73685ebSopenharmony_ci /// Render SVG output 21e73685ebSopenharmony_ci Svg, 22e73685ebSopenharmony_ci /// Render Stderr output 23e73685ebSopenharmony_ci Stderr { 24e73685ebSopenharmony_ci /// Configure coloring of output 25e73685ebSopenharmony_ci #[structopt( 26e73685ebSopenharmony_ci long = "color", 27e73685ebSopenharmony_ci parse(try_from_str), 28e73685ebSopenharmony_ci default_value = "auto", 29e73685ebSopenharmony_ci possible_values = ColorArg::VARIANTS, 30e73685ebSopenharmony_ci case_insensitive = true 31e73685ebSopenharmony_ci )] 32e73685ebSopenharmony_ci color: ColorArg, 33e73685ebSopenharmony_ci }, 34e73685ebSopenharmony_ci} 35e73685ebSopenharmony_ci 36e73685ebSopenharmony_cifn main() -> anyhow::Result<()> { 37e73685ebSopenharmony_ci let file = SimpleFile::new( 38e73685ebSopenharmony_ci "FizzBuzz.fun", 39e73685ebSopenharmony_ci unindent::unindent( 40e73685ebSopenharmony_ci r#" 41e73685ebSopenharmony_ci module FizzBuzz where 42e73685ebSopenharmony_ci 43e73685ebSopenharmony_ci fizz₁ : Nat → String 44e73685ebSopenharmony_ci fizz₁ num = case (mod num 5) (mod num 3) of 45e73685ebSopenharmony_ci 0 0 => "FizzBuzz" 46e73685ebSopenharmony_ci 0 _ => "Fizz" 47e73685ebSopenharmony_ci _ 0 => "Buzz" 48e73685ebSopenharmony_ci _ _ => num 49e73685ebSopenharmony_ci 50e73685ebSopenharmony_ci fizz₂ : Nat → String 51e73685ebSopenharmony_ci fizz₂ num = 52e73685ebSopenharmony_ci case (mod num 5) (mod num 3) of 53e73685ebSopenharmony_ci 0 0 => "FizzBuzz" 54e73685ebSopenharmony_ci 0 _ => "Fizz" 55e73685ebSopenharmony_ci _ 0 => "Buzz" 56e73685ebSopenharmony_ci _ _ => num 57e73685ebSopenharmony_ci "#, 58e73685ebSopenharmony_ci ), 59e73685ebSopenharmony_ci ); 60e73685ebSopenharmony_ci 61e73685ebSopenharmony_ci let diagnostics = [Diagnostic::error() 62e73685ebSopenharmony_ci .with_message("`case` clauses have incompatible types") 63e73685ebSopenharmony_ci .with_code("E0308") 64e73685ebSopenharmony_ci .with_labels(vec![ 65e73685ebSopenharmony_ci Label::primary((), 328..331).with_message("expected `String`, found `Nat`"), 66e73685ebSopenharmony_ci Label::secondary((), 211..331).with_message("`case` clauses have incompatible types"), 67e73685ebSopenharmony_ci Label::secondary((), 258..268).with_message("this is found to be of type `String`"), 68e73685ebSopenharmony_ci Label::secondary((), 284..290).with_message("this is found to be of type `String`"), 69e73685ebSopenharmony_ci Label::secondary((), 306..312).with_message("this is found to be of type `String`"), 70e73685ebSopenharmony_ci Label::secondary((), 186..192).with_message("expected type `String` found here"), 71e73685ebSopenharmony_ci ]) 72e73685ebSopenharmony_ci .with_notes(vec![unindent::unindent( 73e73685ebSopenharmony_ci " 74e73685ebSopenharmony_ci expected type `String` 75e73685ebSopenharmony_ci found type `Nat` 76e73685ebSopenharmony_ci ", 77e73685ebSopenharmony_ci )])]; 78e73685ebSopenharmony_ci 79e73685ebSopenharmony_ci // let mut files = SimpleFiles::new(); 80e73685ebSopenharmony_ci match Opts::from_args() { 81e73685ebSopenharmony_ci Opts::Svg => { 82e73685ebSopenharmony_ci let mut buffer = Vec::new(); 83e73685ebSopenharmony_ci let mut writer = HtmlEscapeWriter::new(SvgWriter::new(&mut buffer)); 84e73685ebSopenharmony_ci let config = codespan_reporting::term::Config { 85e73685ebSopenharmony_ci styles: codespan_reporting::term::Styles::with_blue(Color::Blue), 86e73685ebSopenharmony_ci ..codespan_reporting::term::Config::default() 87e73685ebSopenharmony_ci }; 88e73685ebSopenharmony_ci 89e73685ebSopenharmony_ci for diagnostic in &diagnostics { 90e73685ebSopenharmony_ci term::emit(&mut writer, &config, &file, &diagnostic)?; 91e73685ebSopenharmony_ci } 92e73685ebSopenharmony_ci 93e73685ebSopenharmony_ci let num_lines = buffer.iter().filter(|byte| **byte == b'\n').count() + 1; 94e73685ebSopenharmony_ci 95e73685ebSopenharmony_ci let padding = 10; 96e73685ebSopenharmony_ci let font_size = 12; 97e73685ebSopenharmony_ci let line_spacing = 3; 98e73685ebSopenharmony_ci let width = 882; 99e73685ebSopenharmony_ci let height = padding + num_lines * (font_size + line_spacing) + padding; 100e73685ebSopenharmony_ci 101e73685ebSopenharmony_ci let stdout = std::io::stdout(); 102e73685ebSopenharmony_ci let writer = &mut stdout.lock(); 103e73685ebSopenharmony_ci 104e73685ebSopenharmony_ci write!( 105e73685ebSopenharmony_ci writer, 106e73685ebSopenharmony_ci r#"<svg viewBox="0 0 {width} {height}" xmlns="http://www.w3.org/2000/svg"> 107e73685ebSopenharmony_ci <style> 108e73685ebSopenharmony_ci /* https://github.com/aaron-williamson/base16-alacritty/blob/master/colors/base16-tomorrow-night-256.yml */ 109e73685ebSopenharmony_ci pre {{ 110e73685ebSopenharmony_ci background: #1d1f21; 111e73685ebSopenharmony_ci margin: 0; 112e73685ebSopenharmony_ci padding: {padding}px; 113e73685ebSopenharmony_ci border-radius: 6px; 114e73685ebSopenharmony_ci color: #ffffff; 115e73685ebSopenharmony_ci font: {font_size}px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; 116e73685ebSopenharmony_ci }} 117e73685ebSopenharmony_ci 118e73685ebSopenharmony_ci pre .bold {{ font-weight: bold; }} 119e73685ebSopenharmony_ci 120e73685ebSopenharmony_ci pre .fg.black {{ color: #1d1f21; }} 121e73685ebSopenharmony_ci pre .fg.red {{ color: #cc6666; }} 122e73685ebSopenharmony_ci pre .fg.green {{ color: #b5bd68; }} 123e73685ebSopenharmony_ci pre .fg.yellow {{ color: #f0c674; }} 124e73685ebSopenharmony_ci pre .fg.blue {{ color: #81a2be; }} 125e73685ebSopenharmony_ci pre .fg.magenta {{ color: #b294bb; }} 126e73685ebSopenharmony_ci pre .fg.cyan {{ color: #8abeb7; }} 127e73685ebSopenharmony_ci pre .fg.white {{ color: #c5c8c6; }} 128e73685ebSopenharmony_ci 129e73685ebSopenharmony_ci pre .fg.black.bright {{ color: #969896; }} 130e73685ebSopenharmony_ci pre .fg.red.bright {{ color: #cc6666; }} 131e73685ebSopenharmony_ci pre .fg.green.bright {{ color: #b5bd68; }} 132e73685ebSopenharmony_ci pre .fg.yellow.bright {{ color: #f0c674; }} 133e73685ebSopenharmony_ci pre .fg.blue.bright {{ color: #81a2be; }} 134e73685ebSopenharmony_ci pre .fg.magenta.bright {{ color: #b294bb; }} 135e73685ebSopenharmony_ci pre .fg.cyan.bright {{ color: #8abeb7; }} 136e73685ebSopenharmony_ci pre .fg.white.bright {{ color: #ffffff; }} 137e73685ebSopenharmony_ci 138e73685ebSopenharmony_ci pre .bg.black {{ background-color: #1d1f21; }} 139e73685ebSopenharmony_ci pre .bg.red {{ background-color: #cc6666; }} 140e73685ebSopenharmony_ci pre .bg.green {{ background-color: #b5bd68; }} 141e73685ebSopenharmony_ci pre .bg.yellow {{ background-color: #f0c674; }} 142e73685ebSopenharmony_ci pre .bg.blue {{ background-color: #81a2be; }} 143e73685ebSopenharmony_ci pre .bg.magenta {{ background-color: #b294bb; }} 144e73685ebSopenharmony_ci pre .bg.cyan {{ background-color: #8abeb7; }} 145e73685ebSopenharmony_ci pre .bg.white {{ background-color: #c5c8c6; }} 146e73685ebSopenharmony_ci 147e73685ebSopenharmony_ci pre .bg.black.bright {{ background-color: #969896; }} 148e73685ebSopenharmony_ci pre .bg.red.bright {{ background-color: #cc6666; }} 149e73685ebSopenharmony_ci pre .bg.green.bright {{ background-color: #b5bd68; }} 150e73685ebSopenharmony_ci pre .bg.yellow.bright {{ background-color: #f0c674; }} 151e73685ebSopenharmony_ci pre .bg.blue.bright {{ background-color: #81a2be; }} 152e73685ebSopenharmony_ci pre .bg.magenta.bright {{ background-color: #b294bb; }} 153e73685ebSopenharmony_ci pre .bg.cyan.bright {{ background-color: #8abeb7; }} 154e73685ebSopenharmony_ci pre .bg.white.bright {{ background-color: #ffffff; }} 155e73685ebSopenharmony_ci </style> 156e73685ebSopenharmony_ci 157e73685ebSopenharmony_ci <foreignObject x="0" y="0" width="{width}" height="{height}"> 158e73685ebSopenharmony_ci <div xmlns="http://www.w3.org/1999/xhtml"> 159e73685ebSopenharmony_ci <pre>"#, 160e73685ebSopenharmony_ci padding = padding, 161e73685ebSopenharmony_ci font_size = font_size, 162e73685ebSopenharmony_ci width = width, 163e73685ebSopenharmony_ci height = height, 164e73685ebSopenharmony_ci )?; 165e73685ebSopenharmony_ci 166e73685ebSopenharmony_ci writer.write_all(&buffer)?; 167e73685ebSopenharmony_ci 168e73685ebSopenharmony_ci write!( 169e73685ebSopenharmony_ci writer, 170e73685ebSopenharmony_ci "</pre> 171e73685ebSopenharmony_ci </div> 172e73685ebSopenharmony_ci </foreignObject> 173e73685ebSopenharmony_ci</svg> 174e73685ebSopenharmony_ci" 175e73685ebSopenharmony_ci )?; 176e73685ebSopenharmony_ci } 177e73685ebSopenharmony_ci Opts::Stderr { color } => { 178e73685ebSopenharmony_ci let writer = StandardStream::stderr(color.into()); 179e73685ebSopenharmony_ci let config = codespan_reporting::term::Config::default(); 180e73685ebSopenharmony_ci for diagnostic in &diagnostics { 181e73685ebSopenharmony_ci term::emit(&mut writer.lock(), &config, &file, &diagnostic)?; 182e73685ebSopenharmony_ci } 183e73685ebSopenharmony_ci } 184e73685ebSopenharmony_ci } 185e73685ebSopenharmony_ci 186e73685ebSopenharmony_ci Ok(()) 187e73685ebSopenharmony_ci} 188e73685ebSopenharmony_ci 189e73685ebSopenharmony_ci/// Rudimentary HTML escaper which performs the following conversions: 190e73685ebSopenharmony_ci/// 191e73685ebSopenharmony_ci/// - `<` ⇒ `<` 192e73685ebSopenharmony_ci/// - `>` ⇒ `>` 193e73685ebSopenharmony_ci/// - `&` ⇒ `&` 194e73685ebSopenharmony_cipub struct HtmlEscapeWriter<W> { 195e73685ebSopenharmony_ci upstream: W, 196e73685ebSopenharmony_ci} 197e73685ebSopenharmony_ci 198e73685ebSopenharmony_ciimpl<W> HtmlEscapeWriter<W> { 199e73685ebSopenharmony_ci pub fn new(upstream: W) -> HtmlEscapeWriter<W> { 200e73685ebSopenharmony_ci HtmlEscapeWriter { upstream } 201e73685ebSopenharmony_ci } 202e73685ebSopenharmony_ci} 203e73685ebSopenharmony_ci 204e73685ebSopenharmony_ciimpl<W: Write> Write for HtmlEscapeWriter<W> { 205e73685ebSopenharmony_ci fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 206e73685ebSopenharmony_ci let mut last_term = 0usize; 207e73685ebSopenharmony_ci for (i, byte) in buf.iter().enumerate() { 208e73685ebSopenharmony_ci let escape = match byte { 209e73685ebSopenharmony_ci b'<' => &b"<"[..], 210e73685ebSopenharmony_ci b'>' => &b">"[..], 211e73685ebSopenharmony_ci b'&' => &b"&"[..], 212e73685ebSopenharmony_ci _ => continue, 213e73685ebSopenharmony_ci }; 214e73685ebSopenharmony_ci self.upstream.write_all(&buf[last_term..i])?; 215e73685ebSopenharmony_ci last_term = i + 1; 216e73685ebSopenharmony_ci self.upstream.write_all(escape)?; 217e73685ebSopenharmony_ci } 218e73685ebSopenharmony_ci self.upstream.write_all(&buf[last_term..])?; 219e73685ebSopenharmony_ci Ok(buf.len()) 220e73685ebSopenharmony_ci } 221e73685ebSopenharmony_ci 222e73685ebSopenharmony_ci fn flush(&mut self) -> io::Result<()> { 223e73685ebSopenharmony_ci self.upstream.flush() 224e73685ebSopenharmony_ci } 225e73685ebSopenharmony_ci} 226e73685ebSopenharmony_ci 227e73685ebSopenharmony_ciimpl<W: WriteColor> WriteColor for HtmlEscapeWriter<W> { 228e73685ebSopenharmony_ci fn supports_color(&self) -> bool { 229e73685ebSopenharmony_ci self.upstream.supports_color() 230e73685ebSopenharmony_ci } 231e73685ebSopenharmony_ci 232e73685ebSopenharmony_ci fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { 233e73685ebSopenharmony_ci self.upstream.set_color(spec) 234e73685ebSopenharmony_ci } 235e73685ebSopenharmony_ci 236e73685ebSopenharmony_ci fn reset(&mut self) -> io::Result<()> { 237e73685ebSopenharmony_ci self.upstream.reset() 238e73685ebSopenharmony_ci } 239e73685ebSopenharmony_ci} 240e73685ebSopenharmony_ci 241e73685ebSopenharmony_cipub struct SvgWriter<W> { 242e73685ebSopenharmony_ci upstream: W, 243e73685ebSopenharmony_ci color: ColorSpec, 244e73685ebSopenharmony_ci} 245e73685ebSopenharmony_ci 246e73685ebSopenharmony_ciimpl<W> SvgWriter<W> { 247e73685ebSopenharmony_ci pub fn new(upstream: W) -> SvgWriter<W> { 248e73685ebSopenharmony_ci SvgWriter { 249e73685ebSopenharmony_ci upstream, 250e73685ebSopenharmony_ci color: ColorSpec::new(), 251e73685ebSopenharmony_ci } 252e73685ebSopenharmony_ci } 253e73685ebSopenharmony_ci} 254e73685ebSopenharmony_ci 255e73685ebSopenharmony_ciimpl<W: Write> Write for SvgWriter<W> { 256e73685ebSopenharmony_ci fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 257e73685ebSopenharmony_ci self.upstream.write(buf) 258e73685ebSopenharmony_ci } 259e73685ebSopenharmony_ci 260e73685ebSopenharmony_ci fn flush(&mut self) -> io::Result<()> { 261e73685ebSopenharmony_ci self.upstream.flush() 262e73685ebSopenharmony_ci } 263e73685ebSopenharmony_ci} 264e73685ebSopenharmony_ci 265e73685ebSopenharmony_ciimpl<W: Write> WriteColor for SvgWriter<W> { 266e73685ebSopenharmony_ci fn supports_color(&self) -> bool { 267e73685ebSopenharmony_ci true 268e73685ebSopenharmony_ci } 269e73685ebSopenharmony_ci 270e73685ebSopenharmony_ci fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { 271e73685ebSopenharmony_ci #![allow(unused_assignments)] 272e73685ebSopenharmony_ci 273e73685ebSopenharmony_ci if self.color == *spec { 274e73685ebSopenharmony_ci return Ok(()); 275e73685ebSopenharmony_ci } else { 276e73685ebSopenharmony_ci if !self.color.is_none() { 277e73685ebSopenharmony_ci write!(self, "</span>")?; 278e73685ebSopenharmony_ci } 279e73685ebSopenharmony_ci self.color = spec.clone(); 280e73685ebSopenharmony_ci } 281e73685ebSopenharmony_ci 282e73685ebSopenharmony_ci if spec.is_none() { 283e73685ebSopenharmony_ci write!(self, "</span>")?; 284e73685ebSopenharmony_ci return Ok(()); 285e73685ebSopenharmony_ci } else { 286e73685ebSopenharmony_ci write!(self, "<span class=\"")?; 287e73685ebSopenharmony_ci } 288e73685ebSopenharmony_ci 289e73685ebSopenharmony_ci let mut first = true; 290e73685ebSopenharmony_ci 291e73685ebSopenharmony_ci fn write_first<W: Write>(first: bool, writer: &mut SvgWriter<W>) -> io::Result<bool> { 292e73685ebSopenharmony_ci if !first { 293e73685ebSopenharmony_ci write!(writer, " ")?; 294e73685ebSopenharmony_ci } 295e73685ebSopenharmony_ci 296e73685ebSopenharmony_ci Ok(false) 297e73685ebSopenharmony_ci }; 298e73685ebSopenharmony_ci 299e73685ebSopenharmony_ci fn write_color<W: Write>(color: &Color, writer: &mut SvgWriter<W>) -> io::Result<()> { 300e73685ebSopenharmony_ci match color { 301e73685ebSopenharmony_ci Color::Black => write!(writer, "black"), 302e73685ebSopenharmony_ci Color::Blue => write!(writer, "blue"), 303e73685ebSopenharmony_ci Color::Green => write!(writer, "green"), 304e73685ebSopenharmony_ci Color::Red => write!(writer, "red"), 305e73685ebSopenharmony_ci Color::Cyan => write!(writer, "cyan"), 306e73685ebSopenharmony_ci Color::Magenta => write!(writer, "magenta"), 307e73685ebSopenharmony_ci Color::Yellow => write!(writer, "yellow"), 308e73685ebSopenharmony_ci Color::White => write!(writer, "white"), 309e73685ebSopenharmony_ci // TODO: other colors 310e73685ebSopenharmony_ci _ => Ok(()), 311e73685ebSopenharmony_ci } 312e73685ebSopenharmony_ci }; 313e73685ebSopenharmony_ci 314e73685ebSopenharmony_ci if let Some(fg) = spec.fg() { 315e73685ebSopenharmony_ci first = write_first(first, self)?; 316e73685ebSopenharmony_ci write!(self, "fg ")?; 317e73685ebSopenharmony_ci write_color(fg, self)?; 318e73685ebSopenharmony_ci } 319e73685ebSopenharmony_ci 320e73685ebSopenharmony_ci if let Some(bg) = spec.bg() { 321e73685ebSopenharmony_ci first = write_first(first, self)?; 322e73685ebSopenharmony_ci write!(self, "bg ")?; 323e73685ebSopenharmony_ci write_color(bg, self)?; 324e73685ebSopenharmony_ci } 325e73685ebSopenharmony_ci 326e73685ebSopenharmony_ci if spec.bold() { 327e73685ebSopenharmony_ci first = write_first(first, self)?; 328e73685ebSopenharmony_ci write!(self, "bold")?; 329e73685ebSopenharmony_ci } 330e73685ebSopenharmony_ci 331e73685ebSopenharmony_ci if spec.underline() { 332e73685ebSopenharmony_ci first = write_first(first, self)?; 333e73685ebSopenharmony_ci write!(self, "underline")?; 334e73685ebSopenharmony_ci } 335e73685ebSopenharmony_ci 336e73685ebSopenharmony_ci if spec.intense() { 337e73685ebSopenharmony_ci first = write_first(first, self)?; 338e73685ebSopenharmony_ci write!(self, "bright")?; 339e73685ebSopenharmony_ci } 340e73685ebSopenharmony_ci 341e73685ebSopenharmony_ci write!(self, "\">")?; 342e73685ebSopenharmony_ci 343e73685ebSopenharmony_ci Ok(()) 344e73685ebSopenharmony_ci } 345e73685ebSopenharmony_ci 346e73685ebSopenharmony_ci fn reset(&mut self) -> io::Result<()> { 347e73685ebSopenharmony_ci let color = self.color.clone(); 348e73685ebSopenharmony_ci 349e73685ebSopenharmony_ci if color != ColorSpec::new() { 350e73685ebSopenharmony_ci write!(self, "</span>")?; 351e73685ebSopenharmony_ci self.color = ColorSpec::new(); 352e73685ebSopenharmony_ci } 353e73685ebSopenharmony_ci 354e73685ebSopenharmony_ci Ok(()) 355e73685ebSopenharmony_ci } 356e73685ebSopenharmony_ci} 357