diff --git a/src/app.rs b/src/app.rs index b0e90851..2148d12d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,7 +4,7 @@ use console::Term; use errors::*; use std::collections::HashSet; use std::env; -use style::{OutputComponent, OutputComponents}; +use style::{OutputComponent, OutputComponents, OutputWrap}; #[cfg(windows)] use ansi_term; @@ -86,6 +86,14 @@ impl App { .default_value("auto") .help("When to use the pager"), ) + .arg( + Arg::with_name("wrap") + .long("wrap") + .takes_value(true) + .possible_values(&["character", "never"]) + .default_value("character") + .help("When to wrap text"), + ) .arg( Arg::with_name("list-languages") .long("list-languages") @@ -141,6 +149,16 @@ impl App { true_color: is_truecolor_terminal(), output_components: self.output_components()?, language: self.matches.value_of("language"), + output_wrap: if !self.interactive_output { + // We don't have the tty width when piping to another program. + // There's no point in wrapping when this is the case. + OutputWrap::None + } else { + match self.matches.value_of("wrap") { + Some("character") => OutputWrap::Character, + Some("never") | _ => OutputWrap::None, + } + }, colored_output: match self.matches.value_of("color") { Some("always") => true, Some("never") => false, @@ -197,6 +215,7 @@ impl App { pub struct Config<'a> { pub true_color: bool, + pub output_wrap: OutputWrap, pub output_components: OutputComponents, pub language: Option<&'a str>, pub colored_output: bool, diff --git a/src/decorations.rs b/src/decorations.rs new file mode 100644 index 00000000..bd557064 --- /dev/null +++ b/src/decorations.rs @@ -0,0 +1,154 @@ +use ansi_term::Style; +use diff::LineChange; +use printer::Printer; +use Colors; + +#[derive(Clone)] +pub struct DecorationText { + pub width: usize, + pub text: String, +} + +pub trait Decoration { + fn generate(&self, line_number: usize, continuation: bool, printer: &Printer) + -> DecorationText; + fn width(&self) -> usize; +} + +// Line number decoration. +pub struct LineNumberDecoration { + color: Style, + cached_wrap: DecorationText, + cached_wrap_invalid_at: usize, +} + +impl LineNumberDecoration { + pub fn new(colors: &Colors) -> Self { + LineNumberDecoration { + color: colors.line_number, + cached_wrap_invalid_at: 10000, + cached_wrap: DecorationText { + text: colors.line_number.paint(" ".repeat(4)).to_string(), + width: 4, + }, + } + } +} + +impl Decoration for LineNumberDecoration { + fn generate( + &self, + line_number: usize, + continuation: bool, + _printer: &Printer, + ) -> DecorationText { + if continuation { + if line_number > self.cached_wrap_invalid_at { + let new_width = self.cached_wrap.width + 1; + return DecorationText { + text: self.color.paint(" ".repeat(new_width)).to_string(), + width: new_width, + }; + } + + self.cached_wrap.clone() + } else { + let plain: String = format!("{:4}", line_number); + DecorationText { + width: plain.len(), + text: self.color.paint(plain).to_string(), + } + } + } + + fn width(&self) -> usize { + 4 + } +} + +// Line changes decoration. +pub struct LineChangesDecoration { + cached_none: DecorationText, + cached_added: DecorationText, + cached_removed_above: DecorationText, + cached_removed_below: DecorationText, + cached_modified: DecorationText, +} + +impl LineChangesDecoration { + #[inline] + fn generate_cached(style: Style, text: &str) -> DecorationText { + DecorationText { + text: style.paint(text).to_string(), + width: text.chars().count(), + } + } + + pub fn new(colors: &Colors) -> Self { + LineChangesDecoration { + cached_none: Self::generate_cached(Style::default(), " "), + cached_added: Self::generate_cached(colors.git_added, "+"), + cached_removed_above: Self::generate_cached(colors.git_removed, "‾"), + cached_removed_below: Self::generate_cached(colors.git_removed, "_"), + cached_modified: Self::generate_cached(colors.git_modified, "~"), + } + } +} + +impl Decoration for LineChangesDecoration { + fn generate( + &self, + line_number: usize, + continuation: bool, + printer: &Printer, + ) -> DecorationText { + if !continuation { + if let Some(ref changes) = printer.line_changes { + return match changes.get(&(line_number as u32)) { + Some(&LineChange::Added) => self.cached_added.clone(), + Some(&LineChange::RemovedAbove) => self.cached_removed_above.clone(), + Some(&LineChange::RemovedBelow) => self.cached_removed_below.clone(), + Some(&LineChange::Modified) => self.cached_modified.clone(), + _ => self.cached_none.clone(), + }; + } + } + + self.cached_none.clone() + } + + fn width(&self) -> usize { + self.cached_none.width + } +} + +// Grid border decoration. +pub struct GridBorderDecoration { + cached: DecorationText, +} + +impl GridBorderDecoration { + pub fn new(colors: &Colors) -> Self { + GridBorderDecoration { + cached: DecorationText { + text: colors.grid.paint("│").to_string(), + width: 1, + }, + } + } +} + +impl Decoration for GridBorderDecoration { + fn generate( + &self, + _line_number: usize, + _continuation: bool, + _printer: &Printer, + ) -> DecorationText { + self.cached.clone() + } + + fn width(&self) -> usize { + self.cached.width + } +} diff --git a/src/main.rs b/src/main.rs index 6470622a..1ae22d66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ extern crate syntect; mod app; mod assets; +mod decorations; mod diff; mod printer; mod style; diff --git a/src/printer.rs b/src/printer.rs index ed36ddac..a51cc4dc 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1,18 +1,21 @@ -use ansi_term::Style; use app::Config; -use diff::{LineChange, LineChanges}; +use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration}; +use diff::LineChanges; use errors::*; +use std::boxed::Box; use std::io::Write; +use std::vec::Vec; +use style::OutputWrap; use syntect::highlighting; use terminal::as_terminal_escaped; use Colors; -const PANEL_WIDTH: usize = 7; - pub struct Printer<'a> { handle: &'a mut Write, colors: Colors, config: &'a Config<'a>, + decorations: Vec>, + panel_width: usize, pub line_changes: Option, } @@ -24,10 +27,43 @@ impl<'a> Printer<'a> { Colors::plain() }; + // Create decorations. + let mut decorations: Vec> = Vec::new(); + + if config.output_components.numbers() { + decorations.push(Box::new(LineNumberDecoration::new(&colors))); + } + + if config.output_components.changes() { + decorations.push(Box::new(LineChangesDecoration::new(&colors))); + } + + let mut panel_width: usize = + decorations.len() + decorations.iter().fold(0, |a, x| a + x.width()); + + // The grid border decoration isn't added until after the panel_width calculation, since the + // print_horizontal_line, print_header, and print_footer functions all assume the panel + // width is without the grid border. + if config.output_components.grid() && decorations.len() > 0 { + decorations.push(Box::new(GridBorderDecoration::new(&colors))); + } + + // Disable the panel if the terminal is too small (i.e. can't fit 5 characters with the + // panel showing). + if config.term_width + < (decorations.len() + decorations.iter().fold(0, |a, x| a + x.width())) + 5 + { + decorations.clear(); + panel_width = 0; + } + + // Create printer. Printer { + panel_width, handle, colors, config, + decorations, line_changes: None, } } @@ -43,8 +79,10 @@ impl<'a> Printer<'a> { write!( self.handle, "{}{} ", - " ".repeat(PANEL_WIDTH), - self.colors.grid.paint("│"), + " ".repeat(self.panel_width), + self.colors + .grid + .paint(if self.panel_width > 0 { "│" } else { "" }), )?; } @@ -75,85 +113,123 @@ impl<'a> Printer<'a> { line_number: usize, regions: &[(highlighting::Style, &str)], ) -> Result<()> { - let decorations = vec![ - self.print_line_number(line_number), - self.print_git_marker(line_number), - self.print_line_border(), - Some(as_terminal_escaped( - ®ions, - self.config.true_color, - self.config.colored_output, - )), - ]; + let mut cursor: usize = 0; + let mut cursor_max: usize = self.config.term_width; + let mut panel_wrap: Option = None; - let grid_requested = self.config.output_components.grid(); - write!( - self.handle, - "{}", - decorations - .into_iter() - .filter_map(|dec| if grid_requested { - Some(dec.unwrap_or_else(|| " ".to_owned())) - } else { - dec - }) - .collect::>() - .join(" ") - )?; + // Line decorations. + if self.panel_width > 0 { + let decorations = self + .decorations + .iter() + .map(|ref d| d.generate(line_number, false, self)) + .collect::>(); + + for deco in decorations { + write!(self.handle, "{} ", deco.text)?; + cursor_max -= deco.width + 1; + } + } + + // Line contents. + if self.config.output_wrap == OutputWrap::None { + let true_color = self.config.true_color; + let colored_output = self.config.colored_output; + + write!( + self.handle, + "{}", + regions + .iter() + .map(|&(style, text)| as_terminal_escaped( + style, + text, + true_color, + colored_output, + )) + .collect::>() + .join("") + )?; + } else { + for &(style, text) in regions.iter() { + let text = text.trim_right_matches(|c| c == '\r' || c == '\n'); + let mut chars = text.chars(); + let mut remaining = text.chars().count(); + + while remaining > 0 { + let available = cursor_max - cursor; + + // It fits. + if remaining <= available { + let text = chars.by_ref().take(remaining).collect::(); + cursor += remaining; + + write!( + self.handle, + "{}", + as_terminal_escaped( + style, + &*text, + self.config.true_color, + self.config.colored_output, + ) + )?; + break; + } + + // Generate wrap padding if not already generated. + if panel_wrap.is_none() { + panel_wrap = if self.panel_width > 0 { + Some(format!( + "{} ", + self.decorations + .iter() + .map(|ref d| d.generate(line_number, true, self).text) + .collect::>() + .join(" ") + )) + } else { + Some("".to_string()) + } + } + + // It wraps. + let text = chars.by_ref().take(available).collect::(); + cursor = 0; + remaining -= available; + + write!( + self.handle, + "{}\n{}", + as_terminal_escaped( + style, + &*text, + self.config.true_color, + self.config.colored_output, + ), + panel_wrap.clone().unwrap() + )?; + } + } + + write!(self.handle, "\n")?; + } Ok(()) } - fn print_line_number(&self, line_number: usize) -> Option { - if self.config.output_components.numbers() { - Some( - self.colors - .line_number - .paint(format!("{:4}", line_number)) - .to_string(), - ) - } else if self.config.output_components.grid() { - Some(" ".to_owned()) - } else { - None - } - } - - fn print_git_marker(&self, line_number: usize) -> Option { - if self.config.output_components.changes() { - Some( - if let Some(ref changes) = self.line_changes { - match changes.get(&(line_number as u32)) { - Some(&LineChange::Added) => self.colors.git_added.paint("+"), - Some(&LineChange::RemovedAbove) => self.colors.git_removed.paint("‾"), - Some(&LineChange::RemovedBelow) => self.colors.git_removed.paint("_"), - Some(&LineChange::Modified) => self.colors.git_modified.paint("~"), - _ => Style::default().paint(" "), - } - } else { - Style::default().paint(" ") - }.to_string(), - ) - } else if self.config.output_components.grid() { - Some(" ".to_owned()) - } else { - None - } - } - - fn print_line_border(&self) -> Option { - if self.config.output_components.grid() { - Some(self.colors.grid.paint("│").to_string()) - } else { - None - } - } - fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> { - let hline = "─".repeat(self.config.term_width - (PANEL_WIDTH + 1)); - let hline = format!("{}{}{}", "─".repeat(PANEL_WIDTH), grid_char, hline); - - writeln!(self.handle, "{}", self.colors.grid.paint(hline))?; + if self.panel_width == 0 { + writeln!( + self.handle, + "{}", + self.colors.grid.paint("─".repeat(self.config.term_width)) + )?; + } else { + let hline = "─".repeat(self.config.term_width - (self.panel_width + 1)); + let hline = format!("{}{}{}", "─".repeat(self.panel_width), grid_char, hline); + writeln!(self.handle, "{}", self.colors.grid.paint(hline))?; + } Ok(()) } diff --git a/src/style.rs b/src/style.rs index ce55c7d7..82ba2ea1 100644 --- a/src/style.rs +++ b/src/style.rs @@ -13,6 +13,12 @@ pub enum OutputComponent { Plain, } +#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] +pub enum OutputWrap { + Character, + None, +} + impl OutputComponent { pub fn components(&self, interactive_terminal: bool) -> &'static [OutputComponent] { match *self { diff --git a/src/terminal.rs b/src/terminal.rs index 118bf3a5..4ebf9478 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -1,5 +1,3 @@ -use std::fmt::Write; - use ansi_term::Colour::{Fixed, RGB}; use ansi_term::Style; use syntect::highlighting::{self, FontStyle}; @@ -27,37 +25,33 @@ fn rgb2ansi(r: u8, g: u8, b: u8) -> u8 { } pub fn as_terminal_escaped( - v: &[(highlighting::Style, &str)], + style: highlighting::Style, + text: &str, true_color: bool, colored: bool, ) -> String { - let mut s: String = String::new(); - for &(ref style, text) in v.iter() { - let style = if !colored { - Style::default() + let style = if !colored { + Style::default() + } else { + let color = if true_color { + RGB(style.foreground.r, style.foreground.g, style.foreground.b) } else { - let color = if true_color { - RGB(style.foreground.r, style.foreground.g, style.foreground.b) - } else { - let ansi = rgb2ansi(style.foreground.r, style.foreground.g, style.foreground.b); - Fixed(ansi) - }; - - if style.font_style.contains(FontStyle::BOLD) { - color.bold() - } else if style.font_style.contains(FontStyle::UNDERLINE) { - color.underline() - } else if style.font_style.contains(FontStyle::ITALIC) { - color.italic() - } else { - color.normal() - } + let ansi = rgb2ansi(style.foreground.r, style.foreground.g, style.foreground.b); + Fixed(ansi) }; - write!(s, "{}", style.paint(text)).unwrap(); - } + if style.font_style.contains(FontStyle::BOLD) { + color.bold() + } else if style.font_style.contains(FontStyle::UNDERLINE) { + color.underline() + } else if style.font_style.contains(FontStyle::ITALIC) { + color.italic() + } else { + color.normal() + } + }; - s + style.paint(text).to_string() } #[test] diff --git a/tests/snapshots/output/changes,grid,header.snapshot.txt b/tests/snapshots/output/changes,grid,header.snapshot.txt index cb53d7a8..cf739b4a 100644 --- a/tests/snapshots/output/changes,grid,header.snapshot.txt +++ b/tests/snapshots/output/changes,grid,header.snapshot.txt @@ -1,25 +1,25 @@ -───────┬──────────────────────────────────────────────────────────────────────── - │ File: sample.rs -───────┼──────────────────────────────────────────────────────────────────────── - │ struct Rectangle { - │ width: u32, - │ height: u32, - │ } - │ - _ │ fn main() { - │ let rect1 = Rectangle { width: 30, height: 50 }; - │ - │ println!( - ~ │ "The perimeter of the rectangle is {} pixels.", - ~ │ perimeter(&rect1) - │ ); - │ } - │ - │ fn area(rectangle: &Rectangle) -> u32 { - │ rectangle.width * rectangle.height - │ } - + │ - + │ fn perimeter(rectangle: &Rectangle) -> u32 { - + │ (rectangle.width + rectangle.height) * 2 - + │ } -───────┴──────────────────────────────────────────────────────────────────────── +──┬───────────────────────────────────────────────────────────────────────────── + │ File: sample.rs +──┼───────────────────────────────────────────────────────────────────────────── + │ struct Rectangle { + │ width: u32, + │ height: u32, + │ } + │ +_ │ fn main() { + │ let rect1 = Rectangle { width: 30, height: 50 }; + │ + │ println!( +~ │ "The perimeter of the rectangle is {} pixels.", +~ │ perimeter(&rect1) + │ ); + │ } + │ + │ fn area(rectangle: &Rectangle) -> u32 { + │ rectangle.width * rectangle.height + │ } ++ │ ++ │ fn perimeter(rectangle: &Rectangle) -> u32 { ++ │ (rectangle.width + rectangle.height) * 2 ++ │ } +──┴───────────────────────────────────────────────────────────────────────────── diff --git a/tests/snapshots/output/changes,grid.snapshot.txt b/tests/snapshots/output/changes,grid.snapshot.txt index ff7ea09d..8f20a69e 100644 --- a/tests/snapshots/output/changes,grid.snapshot.txt +++ b/tests/snapshots/output/changes,grid.snapshot.txt @@ -1,22 +1,22 @@ - │ struct Rectangle { - │ width: u32, - │ height: u32, - │ } - │ - _ │ fn main() { - │ let rect1 = Rectangle { width: 30, height: 50 }; - │ - │ println!( - ~ │ "The perimeter of the rectangle is {} pixels.", - ~ │ perimeter(&rect1) - │ ); - │ } - │ - │ fn area(rectangle: &Rectangle) -> u32 { - │ rectangle.width * rectangle.height - │ } - + │ - + │ fn perimeter(rectangle: &Rectangle) -> u32 { - + │ (rectangle.width + rectangle.height) * 2 - + │ } -───────┴──────────────────────────────────────────────────────────────────────── + │ struct Rectangle { + │ width: u32, + │ height: u32, + │ } + │ +_ │ fn main() { + │ let rect1 = Rectangle { width: 30, height: 50 }; + │ + │ println!( +~ │ "The perimeter of the rectangle is {} pixels.", +~ │ perimeter(&rect1) + │ ); + │ } + │ + │ fn area(rectangle: &Rectangle) -> u32 { + │ rectangle.width * rectangle.height + │ } ++ │ ++ │ fn perimeter(rectangle: &Rectangle) -> u32 { ++ │ (rectangle.width + rectangle.height) * 2 ++ │ } +──┴───────────────────────────────────────────────────────────────────────────── diff --git a/tests/snapshots/output/grid,header,numbers.snapshot.txt b/tests/snapshots/output/grid,header,numbers.snapshot.txt index e842b5cb..56696b17 100644 --- a/tests/snapshots/output/grid,header,numbers.snapshot.txt +++ b/tests/snapshots/output/grid,header,numbers.snapshot.txt @@ -1,25 +1,25 @@ -───────┬──────────────────────────────────────────────────────────────────────── - │ File: sample.rs -───────┼──────────────────────────────────────────────────────────────────────── - 1 │ struct Rectangle { - 2 │ width: u32, - 3 │ height: u32, - 4 │ } - 5 │ - 6 │ fn main() { - 7 │ let rect1 = Rectangle { width: 30, height: 50 }; - 8 │ - 9 │ println!( - 10 │ "The perimeter of the rectangle is {} pixels.", - 11 │ perimeter(&rect1) - 12 │ ); - 13 │ } - 14 │ - 15 │ fn area(rectangle: &Rectangle) -> u32 { - 16 │ rectangle.width * rectangle.height - 17 │ } - 18 │ - 19 │ fn perimeter(rectangle: &Rectangle) -> u32 { - 20 │ (rectangle.width + rectangle.height) * 2 - 21 │ } -───────┴──────────────────────────────────────────────────────────────────────── +─────┬────────────────────────────────────────────────────────────────────────── + │ File: sample.rs +─────┼────────────────────────────────────────────────────────────────────────── + 1 │ struct Rectangle { + 2 │ width: u32, + 3 │ height: u32, + 4 │ } + 5 │ + 6 │ fn main() { + 7 │ let rect1 = Rectangle { width: 30, height: 50 }; + 8 │ + 9 │ println!( + 10 │ "The perimeter of the rectangle is {} pixels.", + 11 │ perimeter(&rect1) + 12 │ ); + 13 │ } + 14 │ + 15 │ fn area(rectangle: &Rectangle) -> u32 { + 16 │ rectangle.width * rectangle.height + 17 │ } + 18 │ + 19 │ fn perimeter(rectangle: &Rectangle) -> u32 { + 20 │ (rectangle.width + rectangle.height) * 2 + 21 │ } +─────┴────────────────────────────────────────────────────────────────────────── diff --git a/tests/snapshots/output/grid,header.snapshot.txt b/tests/snapshots/output/grid,header.snapshot.txt index a6fbca2d..781efb81 100644 --- a/tests/snapshots/output/grid,header.snapshot.txt +++ b/tests/snapshots/output/grid,header.snapshot.txt @@ -1,25 +1,25 @@ -───────┬──────────────────────────────────────────────────────────────────────── - │ File: sample.rs -───────┼──────────────────────────────────────────────────────────────────────── - │ struct Rectangle { - │ width: u32, - │ height: u32, - │ } - │ - │ fn main() { - │ let rect1 = Rectangle { width: 30, height: 50 }; - │ - │ println!( - │ "The perimeter of the rectangle is {} pixels.", - │ perimeter(&rect1) - │ ); - │ } - │ - │ fn area(rectangle: &Rectangle) -> u32 { - │ rectangle.width * rectangle.height - │ } - │ - │ fn perimeter(rectangle: &Rectangle) -> u32 { - │ (rectangle.width + rectangle.height) * 2 - │ } -───────┴──────────────────────────────────────────────────────────────────────── +──────────────────────────────────────────────────────────────────────────────── + File: sample.rs +──────────────────────────────────────────────────────────────────────────────── +struct Rectangle { + width: u32, + height: u32, +} + +fn main() { + let rect1 = Rectangle { width: 30, height: 50 }; + + println!( + "The perimeter of the rectangle is {} pixels.", + perimeter(&rect1) + ); +} + +fn area(rectangle: &Rectangle) -> u32 { + rectangle.width * rectangle.height +} + +fn perimeter(rectangle: &Rectangle) -> u32 { + (rectangle.width + rectangle.height) * 2 +} +──────────────────────────────────────────────────────────────────────────────── diff --git a/tests/snapshots/output/grid,numbers.snapshot.txt b/tests/snapshots/output/grid,numbers.snapshot.txt index 0186dac3..2a76f8d3 100644 --- a/tests/snapshots/output/grid,numbers.snapshot.txt +++ b/tests/snapshots/output/grid,numbers.snapshot.txt @@ -1,22 +1,22 @@ - 1 │ struct Rectangle { - 2 │ width: u32, - 3 │ height: u32, - 4 │ } - 5 │ - 6 │ fn main() { - 7 │ let rect1 = Rectangle { width: 30, height: 50 }; - 8 │ - 9 │ println!( - 10 │ "The perimeter of the rectangle is {} pixels.", - 11 │ perimeter(&rect1) - 12 │ ); - 13 │ } - 14 │ - 15 │ fn area(rectangle: &Rectangle) -> u32 { - 16 │ rectangle.width * rectangle.height - 17 │ } - 18 │ - 19 │ fn perimeter(rectangle: &Rectangle) -> u32 { - 20 │ (rectangle.width + rectangle.height) * 2 - 21 │ } -───────┴──────────────────────────────────────────────────────────────────────── + 1 │ struct Rectangle { + 2 │ width: u32, + 3 │ height: u32, + 4 │ } + 5 │ + 6 │ fn main() { + 7 │ let rect1 = Rectangle { width: 30, height: 50 }; + 8 │ + 9 │ println!( + 10 │ "The perimeter of the rectangle is {} pixels.", + 11 │ perimeter(&rect1) + 12 │ ); + 13 │ } + 14 │ + 15 │ fn area(rectangle: &Rectangle) -> u32 { + 16 │ rectangle.width * rectangle.height + 17 │ } + 18 │ + 19 │ fn perimeter(rectangle: &Rectangle) -> u32 { + 20 │ (rectangle.width + rectangle.height) * 2 + 21 │ } +─────┴────────────────────────────────────────────────────────────────────────── diff --git a/tests/snapshots/output/grid.snapshot.txt b/tests/snapshots/output/grid.snapshot.txt index 2adebd8f..b5bd13c2 100644 --- a/tests/snapshots/output/grid.snapshot.txt +++ b/tests/snapshots/output/grid.snapshot.txt @@ -1,22 +1,22 @@ - │ struct Rectangle { - │ width: u32, - │ height: u32, - │ } - │ - │ fn main() { - │ let rect1 = Rectangle { width: 30, height: 50 }; - │ - │ println!( - │ "The perimeter of the rectangle is {} pixels.", - │ perimeter(&rect1) - │ ); - │ } - │ - │ fn area(rectangle: &Rectangle) -> u32 { - │ rectangle.width * rectangle.height - │ } - │ - │ fn perimeter(rectangle: &Rectangle) -> u32 { - │ (rectangle.width + rectangle.height) * 2 - │ } -───────┴──────────────────────────────────────────────────────────────────────── +struct Rectangle { + width: u32, + height: u32, +} + +fn main() { + let rect1 = Rectangle { width: 30, height: 50 }; + + println!( + "The perimeter of the rectangle is {} pixels.", + perimeter(&rect1) + ); +} + +fn area(rectangle: &Rectangle) -> u32 { + rectangle.width * rectangle.height +} + +fn perimeter(rectangle: &Rectangle) -> u32 { + (rectangle.width + rectangle.height) * 2 +} +────────────────────────────────────────────────────────────────────────────────