1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 // ASCII art shape renderer. Demonstrates traits, impls, operator overloading,
12 // non-copyable struct, unit testing. To run execute: rustc --test shapes.rs &&
15 // Rust's std library is tightly bound to the language itself so it is
16 // automatically linked in. However the extra library is designed to be
17 // optional (for code that must run on constrained environments like embedded
18 // devices or special environments like kernel code) so it must be explicitly
21 // Extern mod controls linkage. Use controls the visibility of names to modules
22 // that are already linked in. Using WriterUtil allows us to use the write_line
26 use std::iter::repeat;
29 // Represents a position on a canvas.
30 #[derive(Copy, Clone)]
36 // Represents an offset on a canvas. (This has the same structure as a Point.
37 // but different semantics).
38 #[derive(Copy, Clone)]
44 #[derive(Copy, Clone)]
50 // Contains the information needed to do shape rendering via ASCII art.
55 lines: Vec<Vec<char> > ,
57 // This struct can be quite large so we'll disable copying: developers need
58 // to either pass these structs around via references or move them.
61 impl Drop for AsciiArt {
65 // It's common to define a constructor sort of function to create struct instances.
66 // If there is a canonical constructor it is typically named the same as the type.
67 // Other constructor sort of functions are typically named from_foo, from_bar, etc.
68 fn AsciiArt(width: usize, height: usize, fill: char) -> AsciiArt {
69 // Build a vector of vectors containing blank characters for each position in
71 let lines = vec![vec!['.'; width]; height];
73 // Rust code often returns values by omitting the trailing semi-colon
74 // instead of using an explicit return statement.
75 AsciiArt {width: width, height: height, fill: fill, lines: lines}
78 // Methods particular to the AsciiArt struct.
80 fn add_pt(&mut self, x: isize, y: isize) {
81 if x >= 0 && x < self.width as isize {
82 if y >= 0 && y < self.height as isize {
83 // Note that numeric types don't implicitly convert to each other.
87 // Vector subscripting will normally copy the element, but &v[i]
88 // will return a reference which is what we need because the
90 // 1) potentially large
91 // 2) needs to be modified
92 let row = &mut self.lines[v];
99 // Allows AsciiArt to be converted to a string using the libcore ToString trait.
100 // Note that the %s fmt! specifier will not call this automatically.
101 impl fmt::Display for AsciiArt {
102 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103 // Convert each line into a string.
104 let lines = self.lines.iter()
105 .map(|line| line.iter().cloned().collect())
106 .collect::<Vec<String>>();
108 // Concatenate the lines together using a new-line.
109 write!(f, "{}", lines.join("\n"))
113 // This is similar to an interface in other languages: it defines a protocol which
114 // developers can implement for arbitrary concrete types.
116 fn add_point(&mut self, shape: Point);
117 fn add_rect(&mut self, shape: Rect);
119 // Unlike interfaces traits support default implementations.
120 // Got an ICE as soon as I added this method.
121 fn add_points(&mut self, shapes: &[Point]) {
122 for pt in shapes {self.add_point(*pt)};
126 // Here we provide an implementation of the Canvas methods for AsciiArt.
127 // Other implementations could also be provided (e.g. for PDF or Apple's Quartz)
128 // and code can use them polymorphically via the Canvas trait.
129 impl Canvas for AsciiArt {
130 fn add_point(&mut self, shape: Point) {
131 self.add_pt(shape.x, shape.y);
134 fn add_rect(&mut self, shape: Rect) {
135 // Add the top and bottom lines.
136 for x in shape.top_left.x..shape.top_left.x + shape.size.width {
137 self.add_pt(x, shape.top_left.y);
138 self.add_pt(x, shape.top_left.y + shape.size.height - 1);
141 // Add the left and right lines.
142 for y in shape.top_left.y..shape.top_left.y + shape.size.height {
143 self.add_pt(shape.top_left.x, y);
144 self.add_pt(shape.top_left.x + shape.size.width - 1, y);
149 // Rust's unit testing framework is currently a bit under-developed so we'll use
150 // this little helper.
151 pub fn check_strs(actual: &str, expected: &str) -> bool {
152 if actual != expected {
153 println!("Found:\n{}\nbut expected\n{}", actual, expected);
160 fn test_ascii_art_ctor() {
161 let art = AsciiArt(3, 3, '*');
162 assert!(check_strs(&art.to_string(), "...\n...\n..."));
167 let mut art = AsciiArt(3, 3, '*');
171 assert!(check_strs(&art.to_string(), "*..\n...\n.*."));
176 let mut art = AsciiArt(4, 4, '*');
177 art.add_rect(Rect {top_left: Point {x: 0, y: 0}, size: Size {width: 4, height: 4}});
178 art.add_point(Point {x: 2, y: 2});
179 assert!(check_strs(&art.to_string(), "****\n*..*\n*.**\n****"));
183 test_ascii_art_ctor();