//! Renders the preview SVG for the README. //! //! To update the preview, execute the following command from the top level of //! the repository: //! //! ```sh //! cargo run --example readme_preview svg > codespan-reporting/assets/readme_preview.svg //! ``` use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFile; use codespan_reporting::term::termcolor::{Color, ColorSpec, StandardStream, WriteColor}; use codespan_reporting::term::{self, ColorArg}; use std::io::{self, Write}; use structopt::StructOpt; #[derive(Debug, StructOpt)] #[structopt(name = "emit")] pub enum Opts { /// Render SVG output Svg, /// Render Stderr output Stderr { /// Configure coloring of output #[structopt( long = "color", parse(try_from_str), default_value = "auto", possible_values = ColorArg::VARIANTS, case_insensitive = true )] color: ColorArg, }, } fn main() -> anyhow::Result<()> { let file = SimpleFile::new( "FizzBuzz.fun", unindent::unindent( r#" module FizzBuzz where fizz₁ : Nat → String fizz₁ num = case (mod num 5) (mod num 3) of 0 0 => "FizzBuzz" 0 _ => "Fizz" _ 0 => "Buzz" _ _ => num fizz₂ : Nat → String fizz₂ num = case (mod num 5) (mod num 3) of 0 0 => "FizzBuzz" 0 _ => "Fizz" _ 0 => "Buzz" _ _ => num "#, ), ); let diagnostics = [Diagnostic::error() .with_message("`case` clauses have incompatible types") .with_code("E0308") .with_labels(vec![ Label::primary((), 328..331).with_message("expected `String`, found `Nat`"), Label::secondary((), 211..331).with_message("`case` clauses have incompatible types"), Label::secondary((), 258..268).with_message("this is found to be of type `String`"), Label::secondary((), 284..290).with_message("this is found to be of type `String`"), Label::secondary((), 306..312).with_message("this is found to be of type `String`"), Label::secondary((), 186..192).with_message("expected type `String` found here"), ]) .with_notes(vec![unindent::unindent( " expected type `String` found type `Nat` ", )])]; // let mut files = SimpleFiles::new(); match Opts::from_args() { Opts::Svg => { let mut buffer = Vec::new(); let mut writer = HtmlEscapeWriter::new(SvgWriter::new(&mut buffer)); let config = codespan_reporting::term::Config { styles: codespan_reporting::term::Styles::with_blue(Color::Blue), ..codespan_reporting::term::Config::default() }; for diagnostic in &diagnostics { term::emit(&mut writer, &config, &file, diagnostic)?; } let num_lines = buffer.iter().filter(|byte| **byte == b'\n').count() + 1; let padding = 10; let font_size = 12; let line_spacing = 3; let width = 882; let height = padding + num_lines * (font_size + line_spacing) + padding; let stdout = std::io::stdout(); let writer = &mut stdout.lock(); write!( writer, r#"
"#,
                padding = padding,
                font_size = font_size,
                width = width,
                height = height,
            )?;

            writer.write_all(&buffer)?;

            write!(
                writer,
                "
" )?; } Opts::Stderr { color } => { let writer = StandardStream::stderr(color.into()); let config = codespan_reporting::term::Config::default(); for diagnostic in &diagnostics { term::emit(&mut writer.lock(), &config, &file, diagnostic)?; } } } Ok(()) } /// Rudimentary HTML escaper which performs the following conversions: /// /// - `<` ⇒ `<` /// - `>` ⇒ `>` /// - `&` ⇒ `&` pub struct HtmlEscapeWriter { upstream: W, } impl HtmlEscapeWriter { pub fn new(upstream: W) -> HtmlEscapeWriter { HtmlEscapeWriter { upstream } } } impl Write for HtmlEscapeWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let mut last_term = 0usize; for (i, byte) in buf.iter().enumerate() { let escape = match byte { b'<' => &b"<"[..], b'>' => &b">"[..], b'&' => &b"&"[..], _ => continue, }; self.upstream.write_all(&buf[last_term..i])?; last_term = i + 1; self.upstream.write_all(escape)?; } self.upstream.write_all(&buf[last_term..])?; Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { self.upstream.flush() } } impl WriteColor for HtmlEscapeWriter { fn supports_color(&self) -> bool { self.upstream.supports_color() } fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { self.upstream.set_color(spec) } fn reset(&mut self) -> io::Result<()> { self.upstream.reset() } } pub struct SvgWriter { upstream: W, color: ColorSpec, } impl SvgWriter { pub fn new(upstream: W) -> SvgWriter { SvgWriter { upstream, color: ColorSpec::new(), } } } impl Write for SvgWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.upstream.write(buf) } fn flush(&mut self) -> io::Result<()> { self.upstream.flush() } } impl WriteColor for SvgWriter { fn supports_color(&self) -> bool { true } fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { #![allow(unused_assignments)] if self.color == *spec { return Ok(()); } else { if !self.color.is_none() { write!(self, "")?; } self.color = spec.clone(); } if spec.is_none() { write!(self, "")?; return Ok(()); } else { write!(self, "(first: bool, writer: &mut SvgWriter) -> io::Result { if !first { write!(writer, " ")?; } Ok(false) } fn write_color(color: &Color, writer: &mut SvgWriter) -> io::Result<()> { match color { Color::Black => write!(writer, "black"), Color::Blue => write!(writer, "blue"), Color::Green => write!(writer, "green"), Color::Red => write!(writer, "red"), Color::Cyan => write!(writer, "cyan"), Color::Magenta => write!(writer, "magenta"), Color::Yellow => write!(writer, "yellow"), Color::White => write!(writer, "white"), // TODO: other colors _ => Ok(()), } } if let Some(fg) = spec.fg() { first = write_first(first, self)?; write!(self, "fg ")?; write_color(fg, self)?; } if let Some(bg) = spec.bg() { first = write_first(first, self)?; write!(self, "bg ")?; write_color(bg, self)?; } if spec.bold() { first = write_first(first, self)?; write!(self, "bold")?; } if spec.underline() { first = write_first(first, self)?; write!(self, "underline")?; } if spec.intense() { first = write_first(first, self)?; write!(self, "bright")?; } write!(self, "\">")?; Ok(()) } fn reset(&mut self) -> io::Result<()> { let color = self.color.clone(); if color != ColorSpec::new() { write!(self, "")?; self.color = ColorSpec::new(); } Ok(()) } }