From e85d6ddfe5c7fe1e3b4fdbc1389a6f1d2c1eb806 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Tue, 28 Nov 2017 11:32:33 -0800 Subject: [PATCH] Initial commit --- README.md | 125 ++++++ ascii/.gitignore | 1 + ascii/Cargo.lock | 4 + ascii/Cargo.toml | 6 + ascii/src/lib.rs | 101 +++++ basic-router/.gitignore | 1 + basic-router/Cargo.lock | 4 + basic-router/Cargo.toml | 6 + basic-router/src/lib.rs | 89 ++++ binary-tree/.gitignore | 1 + binary-tree/Cargo.lock | 48 +++ binary-tree/Cargo.toml | 7 + binary-tree/src/lib.rs | 286 +++++++++++++ complex/.gitignore | 1 + complex/Cargo.lock | 4 + complex/Cargo.toml | 6 + complex/src/lib.rs | 192 +++++++++ copy/.gitignore | 1 + copy/Cargo.lock | 4 + copy/Cargo.toml | 6 + copy/src/main.rs | 110 +++++ echo-server/.gitignore | 1 + echo-server/Cargo.lock | 4 + echo-server/Cargo.toml | 6 + echo-server/src/main.rs | 27 ++ fern_sim/.gitignore | 1 + fern_sim/Cargo.lock | 4 + fern_sim/Cargo.toml | 13 + fern_sim/src/cells.rs | 12 + fern_sim/src/lib.rs | 13 + fern_sim/src/net.rs | 17 + fern_sim/src/plant_structures/leaves.rs | 6 + fern_sim/src/plant_structures/mod.rs | 45 ++ fern_sim/src/plant_structures/roots.rs | 7 + fern_sim/src/plant_structures/stems.rs | 10 + fern_sim/src/simulation.rs | 52 +++ fern_sim/src/spores.rs | 28 ++ fern_sim/tests/basic/empty.tm | 1 + fern_sim/tests/unfurl.rs | 14 + fern_sim/tests/unfurl_files/fiddlehead.tm | 1 + gap-buffer/.gitignore | 1 + gap-buffer/Cargo.lock | 4 + gap-buffer/Cargo.toml | 6 + gap-buffer/src/lib.rs | 312 ++++++++++++++ gcd/.gitignore | 1 + gcd/Cargo.lock | 4 + gcd/Cargo.toml | 6 + gcd/src/main.rs | 46 +++ generic-queue/.gitignore | 1 + generic-queue/Cargo.lock | 4 + generic-queue/Cargo.toml | 6 + generic-queue/src/lib.rs | 90 ++++ grep/.gitignore | 1 + grep/Cargo.lock | 4 + grep/Cargo.toml | 6 + grep/src/main.rs | 49 +++ http-get/.gitignore | 1 + http-get/Cargo.lock | 476 ++++++++++++++++++++++ http-get/Cargo.toml | 7 + http-get/src/main.rs | 30 ++ interval/.gitignore | 1 + interval/Cargo.lock | 4 + interval/Cargo.toml | 6 + interval/src/lib.rs | 30 ++ iron-gcd/.gitignore | 1 + iron-gcd/Cargo.lock | 382 +++++++++++++++++ iron-gcd/Cargo.toml | 10 + iron-gcd/README.md | 5 + iron-gcd/src/main.rs | 101 +++++ json-macro/.gitignore | 1 + json-macro/Cargo.lock | 4 + json-macro/Cargo.toml | 6 + json-macro/src/lib.rs | 45 ++ json-macro/src/macros.rs | 145 +++++++ libgit2-rs-safe/.gitignore | 1 + libgit2-rs-safe/Cargo.lock | 14 + libgit2-rs-safe/Cargo.toml | 8 + libgit2-rs-safe/build.rs | 3 + libgit2-rs-safe/src/git/mod.rs | 251 ++++++++++++ libgit2-rs-safe/src/git/raw.rs | 56 +++ libgit2-rs-safe/src/main.rs | 24 ++ libgit2-rs/.gitignore | 1 + libgit2-rs/Cargo.lock | 4 + libgit2-rs/Cargo.toml | 7 + libgit2-rs/build.rs | 3 + libgit2-rs/src/main.rs | 67 +++ libgit2-rs/src/raw.rs | 55 +++ queue/.gitignore | 1 + queue/Cargo.lock | 4 + queue/Cargo.toml | 6 + queue/src/lib.rs | 109 +++++ ref-with-flag/.gitignore | 1 + ref-with-flag/Cargo.lock | 4 + ref-with-flag/Cargo.toml | 6 + ref-with-flag/src/lib.rs | 50 +++ 95 files changed, 3736 insertions(+) create mode 100644 README.md create mode 100644 ascii/.gitignore create mode 100644 ascii/Cargo.lock create mode 100644 ascii/Cargo.toml create mode 100644 ascii/src/lib.rs create mode 100644 basic-router/.gitignore create mode 100644 basic-router/Cargo.lock create mode 100644 basic-router/Cargo.toml create mode 100644 basic-router/src/lib.rs create mode 100644 binary-tree/.gitignore create mode 100644 binary-tree/Cargo.lock create mode 100644 binary-tree/Cargo.toml create mode 100644 binary-tree/src/lib.rs create mode 100644 complex/.gitignore create mode 100644 complex/Cargo.lock create mode 100644 complex/Cargo.toml create mode 100644 complex/src/lib.rs create mode 100644 copy/.gitignore create mode 100644 copy/Cargo.lock create mode 100644 copy/Cargo.toml create mode 100644 copy/src/main.rs create mode 100644 echo-server/.gitignore create mode 100644 echo-server/Cargo.lock create mode 100644 echo-server/Cargo.toml create mode 100644 echo-server/src/main.rs create mode 100644 fern_sim/.gitignore create mode 100644 fern_sim/Cargo.lock create mode 100644 fern_sim/Cargo.toml create mode 100644 fern_sim/src/cells.rs create mode 100644 fern_sim/src/lib.rs create mode 100644 fern_sim/src/net.rs create mode 100644 fern_sim/src/plant_structures/leaves.rs create mode 100644 fern_sim/src/plant_structures/mod.rs create mode 100644 fern_sim/src/plant_structures/roots.rs create mode 100644 fern_sim/src/plant_structures/stems.rs create mode 100644 fern_sim/src/simulation.rs create mode 100644 fern_sim/src/spores.rs create mode 100644 fern_sim/tests/basic/empty.tm create mode 100644 fern_sim/tests/unfurl.rs create mode 100644 fern_sim/tests/unfurl_files/fiddlehead.tm create mode 100644 gap-buffer/.gitignore create mode 100644 gap-buffer/Cargo.lock create mode 100644 gap-buffer/Cargo.toml create mode 100644 gap-buffer/src/lib.rs create mode 100644 gcd/.gitignore create mode 100644 gcd/Cargo.lock create mode 100644 gcd/Cargo.toml create mode 100644 gcd/src/main.rs create mode 100644 generic-queue/.gitignore create mode 100644 generic-queue/Cargo.lock create mode 100644 generic-queue/Cargo.toml create mode 100644 generic-queue/src/lib.rs create mode 100644 grep/.gitignore create mode 100644 grep/Cargo.lock create mode 100644 grep/Cargo.toml create mode 100644 grep/src/main.rs create mode 100644 http-get/.gitignore create mode 100644 http-get/Cargo.lock create mode 100644 http-get/Cargo.toml create mode 100644 http-get/src/main.rs create mode 100644 interval/.gitignore create mode 100644 interval/Cargo.lock create mode 100644 interval/Cargo.toml create mode 100644 interval/src/lib.rs create mode 100644 iron-gcd/.gitignore create mode 100644 iron-gcd/Cargo.lock create mode 100644 iron-gcd/Cargo.toml create mode 100644 iron-gcd/README.md create mode 100644 iron-gcd/src/main.rs create mode 100644 json-macro/.gitignore create mode 100644 json-macro/Cargo.lock create mode 100644 json-macro/Cargo.toml create mode 100644 json-macro/src/lib.rs create mode 100644 json-macro/src/macros.rs create mode 100644 libgit2-rs-safe/.gitignore create mode 100644 libgit2-rs-safe/Cargo.lock create mode 100644 libgit2-rs-safe/Cargo.toml create mode 100644 libgit2-rs-safe/build.rs create mode 100644 libgit2-rs-safe/src/git/mod.rs create mode 100644 libgit2-rs-safe/src/git/raw.rs create mode 100644 libgit2-rs-safe/src/main.rs create mode 100644 libgit2-rs/.gitignore create mode 100644 libgit2-rs/Cargo.lock create mode 100644 libgit2-rs/Cargo.toml create mode 100644 libgit2-rs/build.rs create mode 100644 libgit2-rs/src/main.rs create mode 100644 libgit2-rs/src/raw.rs create mode 100644 queue/.gitignore create mode 100644 queue/Cargo.lock create mode 100644 queue/Cargo.toml create mode 100644 queue/src/lib.rs create mode 100644 ref-with-flag/.gitignore create mode 100644 ref-with-flag/Cargo.lock create mode 100644 ref-with-flag/Cargo.toml create mode 100644 ref-with-flag/src/lib.rs diff --git a/README.md b/README.md new file mode 100644 index 0000000..40aec7a --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ +# Code Examples for _Programming Rust_ + +This repository contains complete code for the larger example programs +from the book “Programming Rust”, by Jim Blandy and Jason Orendorff. + +Each subdirectory is a distinct Rust project, with its own Cargo.toml file. You +should be able to enter each directory and use `cargo build` and `cargo test`. +For those projects that define programs, `cargo run` should run them. + +## Chapter 2: A Tour of Rust + +- The `gcd` directory holds the command-line program for computing the greatest + common denominator of a list of numbers. + +- The `iron-gcd` directory holds the code for the simple web service, + implemented using the [`iron`] framework, that computes greatest common + denominators. + +- The Mandelbrot plotting program has its own repository, at + `https://github.com/ProgrammingRust/mandelbrot`. This repository contains + several branches, each showing a different implementation strategy. The + `single-threaded` branch holds the code for the single-threaded version, and + the `bands` branch holds the multi-threaded version. Chapter 19, + “Concurrency”, shows several other approaches, which appear on other branches; + see the repository's [README.md][mandel-readme] file for details. + +[`iron`]: https://crates.io/crates/iron + +## Chapter 8: Crates and Modules + +- We did not actually write a fern simulator. Please accept our sincere apology + for this feckless deception. But the skeleton of modules and definitions we + show in the book is in the `fern_sim` subdirectory. + +## Chapter 9: Structs + +- The `queue` directory holds a library that defines the `Queue` type, + representing a queue of `char` values. + +- The `generic-queue` directory holds code for generic `Queue` type. + +## Chapter 10: Enums and Patterns + +- The `binary-tree` directory holds the source code for the `BinaryTree` type + that appears in the “Generic Enums” and “Populating a Binary Tree” sections. + +## Chapter 12: Operator Overloading + +- The `complex` directory holds the `Complex` type used as a running example + throughout the chapter. + +- The `interval` directory holds the `Interval` type for which the book + implements the `std::cmp::PartialOrd` trait. + +## Chapter 14: Closures + +- The 'basic-router' directory holds the `BasicRouter` type used as an example + in the “Callbacks” section. + +## Chapter 15: Iterators + +- The `binary-tree` directory holds the implementation of the `Iterator` trait + for the `BinaryTree` type originally defined in the “Enums and Patterns” + chapter. + +## Chapter 17: Strings and Text + +- The `complex` directory includes the implementation of the `std::fmt::Display` + formatting trait for a complex number type, shown in the section “Formatting + Your Own Types”. + +## Chapter 18: Input and Output + +- The `grep` directory holds the simple grep-like program shown in the section + “Reading Lines”. + +- The `copy` directory holds the program for copying directory trees from the + section “Reading Directories”, including the additions shown in the next + section, “Platform-Specific Features”. + +- The `echo-server` directory holds the simple network service shown in the + “Networking” section. + +- The `http-get` directory holds the command-line program that uses the + `reqwest` crate to carry out an HTTP request. + +## Chapter 19: Concurrency + +- The search engine used as a running example throughout the book has its own + repository, at `https://github.com/ProgrammingRust/fingertips`. + +- The Mandelbrot set plotter discussed in the section “Revisiting the Mandelbrot + Set” also has its own repository, at `https://github.com/ProgrammingRust/mandelbrot`. + The repository includes several branches exploring different implementations; + see the repository's [README.md][mandel-readme] file for details. + +[mandel-readme]: https://github.com/ProgrammingRust/mandelbrot/blob/master/README.md + +## Chapter 20: Macros + +- The `json-macro` directory holds the definition of the `json!` macro built in + the section “The json! Macro”. + +## Chapter 21: Unsafe Code + +- The `ascii` directory holds the `Ascii` type used as an example in the + sections “Unsafe Blocks” and “Unsafe Functions”. + +- The `ref-with-flag` directory holds the `RefWithFlag` type from the “Raw + Pointers” section. + +- The `gap-buffer` directory holds the `GapBuffer` type, used in the “Raw + Pointers” section to illustrate pointer arithmetic and `std::ptr::read` and + `std::ptr::write`. + +- The `libgit2-rs` and `libgit2-rs-safe` directories contain the two versions of + the program that uses Rust's foreign function interface to call functions from + the `libgit2` C library. The version in `libgit2-rs` is written as a single + giant block of unsafe code, whereas the version in `libgit2-rs-safe` + implements a safe Rust interface to the same functionality, using Rust's type + system to enforce libgit2's rules for proper use of the library. + + Note that both of these require you to have a copy of `libgit2` present on + your system. The chapter provides detailed instructions for building the + correct version, for Linux, macOS, and Microsoft Windows. diff --git a/ascii/.gitignore b/ascii/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/ascii/.gitignore @@ -0,0 +1 @@ +target diff --git a/ascii/Cargo.lock b/ascii/Cargo.lock new file mode 100644 index 0000000..90b2c7c --- /dev/null +++ b/ascii/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "ascii" +version = "0.1.0" + diff --git a/ascii/Cargo.toml b/ascii/Cargo.toml new file mode 100644 index 0000000..9b936b5 --- /dev/null +++ b/ascii/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "ascii" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/ascii/src/lib.rs b/ascii/src/lib.rs new file mode 100644 index 0000000..e406af9 --- /dev/null +++ b/ascii/src/lib.rs @@ -0,0 +1,101 @@ +mod my_ascii { + use std::ascii::AsciiExt; // for u8::is_ascii + + /// An ASCII-encoded string. + #[derive(Debug, Eq, PartialEq)] + pub struct Ascii( + // This must hold only well-formed ASCII text: + // bytes from `0` to `0x7f`. + Vec + ); + + impl Ascii { + /// Create an `Ascii` from the ASCII text in `bytes`. Return a + /// `NotAsciiError` error if `bytes` contains any non-ASCII + /// characters. + pub fn from_bytes(bytes: Vec) -> Result { + if bytes.iter().any(|&byte| !byte.is_ascii()) { + return Err(NotAsciiError(bytes)); + } + Ok(Ascii(bytes)) + } + } + + // When conversion fails, we give back the vector we couldn't convert. + // This should implement `std::error::Error`; omitted for brevity. + #[derive(Debug, Eq, PartialEq)] + pub struct NotAsciiError(pub Vec); + + // Safe, efficient conversion, implemented using unsafe code. + impl From for String { + fn from(ascii: Ascii) -> String { + // If this module has no bugs, this is safe, because + // well-formed ASCII text is also well-formed UTF-8. + unsafe { + String::from_utf8_unchecked(ascii.0) + } + } + } + + impl From for Vec { + fn from(ascii: Ascii) -> Vec { ascii.0 } + } + + // This must be placed inside the `my_ascii` module. + impl Ascii { + /// Construct an `Ascii` value from `bytes`, without checking + /// whether `bytes` actually contains well-formed ASCII. + /// + /// This constructor is infallible, and returns an `Ascii` directly, + /// rather than a `Result` as the `from_bytes` + /// constructor does. + /// + /// # Safety + /// + /// The caller must ensure that `bytes` contains only ASCII + /// characters: bytes no greater than 0x7f. Otherwise, the effect is + /// undefined. + pub unsafe fn from_bytes_unchecked(bytes: Vec) -> Ascii { + Ascii(bytes) + } + } +} + + +#[test] +fn good_ascii() { + use self::my_ascii::Ascii; + + let bytes: Vec = b"ASCII and ye shall receive".to_vec(); + + // This call entails no allocation or text copies, just a scan. + let ascii: Ascii = Ascii::from_bytes(bytes) + .unwrap(); // We know these chosen bytes are legit. + + // This call is zero-cost: no allocation, copies, or scans. + let string = String::from(ascii); + + assert_eq!(string, "ASCII and ye shall receive"); +} + + +#[test] +fn bad_ascii() { + use self::my_ascii::Ascii; + + // Imagine that this vector is the result of some complicated process + // that we expected to produce ASCII. Something went wrong! + let bytes = vec![0xf7, 0xbf, 0xbf, 0xbf]; + + let ascii = unsafe { + // This unsafe function's contract is violated + // when `bytes` holds non-ASCII bytes. + Ascii::from_bytes_unchecked(bytes) + }; + + let bogus: String = ascii.into(); + + // `bogus` now holds ill-formed UTF-8. Parsing its first character + // produces a `char` that is not a valid Unicode code point. + assert_eq!(bogus.chars().next().unwrap() as u32, 0x1fffff); +} diff --git a/basic-router/.gitignore b/basic-router/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/basic-router/.gitignore @@ -0,0 +1 @@ +target diff --git a/basic-router/Cargo.lock b/basic-router/Cargo.lock new file mode 100644 index 0000000..8a74adc --- /dev/null +++ b/basic-router/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "basic-router" +version = "0.1.0" + diff --git a/basic-router/Cargo.toml b/basic-router/Cargo.toml new file mode 100644 index 0000000..d5b345f --- /dev/null +++ b/basic-router/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "basic-router" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/basic-router/src/lib.rs b/basic-router/src/lib.rs new file mode 100644 index 0000000..0d78483 --- /dev/null +++ b/basic-router/src/lib.rs @@ -0,0 +1,89 @@ +#![allow(dead_code)] + +use std::collections::HashMap; + +struct Request { + method: String, + url: String, + headers: HashMap, + body: Vec +} + +struct Response { + code: u32, + headers: HashMap, + body: Vec +} + +type BoxedCallback = Box Response>; + +struct BasicRouter { + routes: HashMap +} + +impl BasicRouter { + // Create an empty router. + fn new() -> BasicRouter { + BasicRouter { routes: HashMap::new() } + } + + // Add a route to the router. + fn add_route(&mut self, url: &str, callback: C) + where C: Fn(&Request) -> Response + 'static + { + self.routes.insert(url.to_string(), Box::new(callback)); + } +} + +impl BasicRouter { + fn handle_request(&self, request: &Request) -> Response { + match self.routes.get(&request.url) { + None => not_found_response(), + Some(callback) => callback(request) + } + } +} + +fn not_found_response() -> Response { + Response { + code: 404, + headers: HashMap::new(), + body: b"

Page not found

".to_vec() + } +} + +fn get_form_response() -> Response { + Response { + code: 200, + headers: HashMap::new(), + body: b"
".to_vec() + } +} + +fn get_gcd_response(_req: &Request) -> Response { + Response { + code: 500, + headers: HashMap::new(), + body: b"

Internal server error

".to_vec() + } +} + +fn req(url: &str) -> Request { + Request { + method: "GET".to_string(), + url: url.to_string(), + headers: HashMap::new(), + body: vec![] + } +} + +#[test] +fn test_router() { + let mut router = BasicRouter::new(); + router.add_route("/", |_| get_form_response()); + router.add_route("/gcd", |req| get_gcd_response(req)); + + assert_eq!(router.handle_request(&req("/piano")).code, 404); + assert_eq!(router.handle_request(&req("/")).code, 200); + assert_eq!(router.handle_request(&req("/gcd")).code, 500); +} diff --git a/binary-tree/.gitignore b/binary-tree/.gitignore new file mode 100644 index 0000000..1de5659 --- /dev/null +++ b/binary-tree/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/binary-tree/Cargo.lock b/binary-tree/Cargo.lock new file mode 100644 index 0000000..f6b7fbe --- /dev/null +++ b/binary-tree/Cargo.lock @@ -0,0 +1,48 @@ +[[package]] +name = "binary-tree" +version = "0.1.0" +dependencies = [ + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-zircon" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" +"checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159" +"checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82" +"checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2" +"checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd" diff --git a/binary-tree/Cargo.toml b/binary-tree/Cargo.toml new file mode 100644 index 0000000..7b3c5a5 --- /dev/null +++ b/binary-tree/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "binary-tree" +version = "0.1.0" +authors = ["You "] + +[dependencies] +rand = "0.3.14" diff --git a/binary-tree/src/lib.rs b/binary-tree/src/lib.rs new file mode 100644 index 0000000..1692afc --- /dev/null +++ b/binary-tree/src/lib.rs @@ -0,0 +1,286 @@ +#![allow(dead_code)] + +extern crate rand; + +// An ordered collection of `T`s. +enum BinaryTree { + Empty, + NonEmpty(Box>) +} + +// A part of a BinaryTree. +struct TreeNode { + element: T, + left: BinaryTree, + right: BinaryTree +} + +#[test] +fn binary_tree_size() { + use std::mem::size_of; + + let word = size_of::(); + assert_eq!(size_of::>(), word); + type Triple = (&'static str, BinaryTree<&'static str>, BinaryTree<&'static str>); + assert_eq!(size_of::(), 4 * word); +} + +#[test] +fn test_hand_building_tree_of_planets() { + use self::BinaryTree::*; + let jupiter_tree = NonEmpty(Box::new(TreeNode { + element: "Jupiter", + left: Empty, + right: Empty + })); + let mercury_tree = NonEmpty(Box::new(TreeNode { + element: "Mercury", + left: Empty, + right: Empty + })); + let mars_tree = NonEmpty(Box::new(TreeNode { + element: "Mars", + left: jupiter_tree, + right: mercury_tree + })); + let venus_tree = NonEmpty(Box::new(TreeNode { + element: "Venus", + left: Empty, + right: Empty + })); + let uranus_tree = NonEmpty(Box::new(TreeNode { + element: "Uranus", + left: Empty, + right: venus_tree + })); + let tree = NonEmpty(Box::new(TreeNode { + element: "Saturn", + left: mars_tree, + right: uranus_tree + })); + + assert_eq!(tree.walk(), + vec!["Jupiter", "Mars", "Mercury", "Saturn", "Uranus", "Venus"]); +} + +impl BinaryTree { + fn walk(&self) -> Vec { + match *self { + BinaryTree::Empty => vec![], + BinaryTree::NonEmpty(ref boxed) => { + let mut result = boxed.left.walk(); + result.push(boxed.element.clone()); + result.extend(boxed.right.walk()); + result + } + } + } +} + +impl BinaryTree { + fn add(&mut self, value: T) { + match *self { + BinaryTree::Empty => + *self = BinaryTree::NonEmpty(Box::new(TreeNode { + element: value, + left: BinaryTree::Empty, + right: BinaryTree::Empty + })), + BinaryTree::NonEmpty(ref mut node) => + if value <= node.element { + node.left.add(value); + } else { + node.right.add(value); + } + } + } +} + +#[test] +fn test_add_method_1() { + let planets = vec!["Mercury", "Venus", "Mars", "Jupiter", "Saturn", "Uranus"]; + let mut tree = BinaryTree::Empty; + for planet in planets { + tree.add(planet); + } + + assert_eq!(tree.walk(), + vec!["Jupiter", "Mars", "Mercury", "Saturn", "Uranus", "Venus"]); +} + +#[test] +fn test_add_method_2() { + let mut tree = BinaryTree::Empty; + tree.add("Mercury"); + tree.add("Venus"); + for planet in vec!["Mars", "Jupiter", "Saturn", "Uranus"] { + tree.add(planet); + } + + assert_eq!(tree.walk(), + vec!["Jupiter", "Mars", "Mercury", "Saturn", "Uranus", "Venus"]); +} + +use self::BinaryTree::*; + +// The state of an in-order traversal of a `BinaryTree`. +struct TreeIter<'a, T: 'a> { + // A stack of references to tree nodes. Since we use `Vec`'s + // `push` and `pop` methods, the top of the stack is the end of the + // vector. + // + // The node the iterator will visit next is at the top of the stack, + // with those ancestors still unvisited below it. If the stack is empty, + // the iteration is over. + unvisited: Vec<&'a TreeNode> +} + +impl<'a, T: 'a> TreeIter<'a, T> { + fn push_left_edge(&mut self, mut tree: &'a BinaryTree) { + while let NonEmpty(ref node) = *tree { + self.unvisited.push(node); + tree = &node.left; + } + } +} + +impl BinaryTree { + fn iter(&self) -> TreeIter { + let mut iter = TreeIter { unvisited: Vec::new() }; + iter.push_left_edge(self); + iter + } +} + +impl<'a, T: 'a> IntoIterator for &'a BinaryTree { + type Item = &'a T; + type IntoIter = TreeIter<'a, T>; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, T> Iterator for TreeIter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option<&'a T> { + // Find the node this iteration must produce, + // or finish the iteration. + let node = match self.unvisited.pop() { + None => return None, + Some(n) => n + }; + + // The next node after this one is the leftmost child of + // this node's right child, so push the path from here down. + self.push_left_edge(&node.right); + + // Produce a reference to this node's value. + Some(&node.element) + } +} + +#[test] +fn external_iterator() { + fn make_node(left: BinaryTree, element: T, right: BinaryTree) + -> BinaryTree + { + NonEmpty(Box::new(TreeNode { left, element, right })) + } + + // Build a small tree. + let subtree_l = make_node(Empty, "mecha", Empty); + let subtree_rl = make_node(Empty, "droid", Empty); + let subtree_r = make_node(subtree_rl, "robot", Empty); + let tree = make_node(subtree_l, "Jaeger", subtree_r); + + // Iterate over it. + let mut v = Vec::new(); + for kind in &tree { + v.push(*kind); + } + assert_eq!(v, ["mecha", "Jaeger", "droid", "robot"]); + + let left_subtree = make_node(Empty, "mecha", Empty); + let right_subtree = make_node(make_node(Empty, "droid", Empty), + "robot", + Empty); + let tree = make_node(left_subtree, "Jaeger", right_subtree); + + let mut v = Vec::new(); + let mut iter = TreeIter { unvisited: vec![] }; + iter.push_left_edge(&tree); + for kind in iter { + v.push(*kind); + } + assert_eq!(v, ["mecha", "Jaeger", "droid", "robot"]); + + let mut v = Vec::new(); + for kind in &tree { + v.push(*kind); + } + assert_eq!(v, ["mecha", "Jaeger", "droid", "robot"]); + + let mut v = Vec::new(); + let mut state = tree.into_iter(); + while let Some(kind) = state.next() { + v.push(*kind); + } + assert_eq!(v, ["mecha", "Jaeger", "droid", "robot"]); + + assert_eq!(tree.iter() + .map(|name| format!("mega-{}", name)) + .collect::>(), + vec!["mega-mecha", "mega-Jaeger", + "mega-droid", "mega-robot"]); + + let mut iterator = tree.into_iter(); + assert_eq!(iterator.next(), Some(&"mecha")); + assert_eq!(iterator.next(), Some(&"Jaeger")); + assert_eq!(iterator.next(), Some(&"droid")); + assert_eq!(iterator.next(), Some(&"robot")); + assert_eq!(iterator.next(), None); +} + +#[test] +fn other_cloned() { + use std::collections::BTreeSet; + + let mut set = BTreeSet::new(); + set.insert("mecha"); + set.insert("Jaeger"); + set.insert("droid"); + set.insert("robot"); + assert_eq!(set.iter().cloned().collect::>(), + ["Jaeger", "droid", "mecha", "robot"]); +} + +#[test] +fn fuzz() { + fn make_random_tree(p: f32) -> BinaryTree { + use rand::{ThreadRng, thread_rng}; + use rand::distributions::range::Range; + use rand::distributions::Sample; + + fn make(p: f32, next: &mut i32, rng: &mut ThreadRng) -> BinaryTree { + let mut range = Range::new(0.0, 1.0); + if range.sample(rng) > p { + Empty + } else { + let left = make(p * p, next, rng); + let element = *next; + *next += 1; + let right = make(p * p, next, rng); + NonEmpty(Box::new(TreeNode { left, element, right })) + } + } + + make(p, &mut 0, &mut thread_rng()) + } + + for _ in 0..100 { + let tree = make_random_tree(0.9999); + assert!(tree.into_iter().fold(Some(0), |s, &i| { + s.and_then(|expected| if i == expected { Some(expected+1) } else { None }) + }).is_some()); + } +} diff --git a/complex/.gitignore b/complex/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/complex/.gitignore @@ -0,0 +1 @@ +target diff --git a/complex/Cargo.lock b/complex/Cargo.lock new file mode 100644 index 0000000..4fa241c --- /dev/null +++ b/complex/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "complex" +version = "0.1.0" + diff --git a/complex/Cargo.toml b/complex/Cargo.toml new file mode 100644 index 0000000..f95d50b --- /dev/null +++ b/complex/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "complex" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/complex/src/lib.rs b/complex/src/lib.rs new file mode 100644 index 0000000..f04c050 --- /dev/null +++ b/complex/src/lib.rs @@ -0,0 +1,192 @@ +#[derive(Clone, Copy, Debug)] +struct Complex { + /// Real portion of the complex number + re: T, + + /// Imaginary portion of the complex number + im: T +} + +use std::ops::Add; +use std::ops::Mul; + +#[cfg(skip)] +impl Add for Complex { + type Output = Complex; + fn add(self, rhs: Self) -> Self { + Complex { re: self.re + rhs.re, im: self.im + rhs.im } + } +} + +impl Add for Complex + where T: Add +{ + type Output = Self; + fn add(self, rhs: Self) -> Self { + Complex { re: self.re + rhs.re, im: self.im + rhs.im } + } +} + +#[cfg(skip)] +impl Add> for Complex + where L: Add +{ + type Output = Complex; + fn add(self, rhs: Complex) -> Self::Output { + Complex { re: self.re + rhs.re, im: self.im + rhs.im } + } +} + +#[cfg(skip)] +impl<'a, P, Rhs> Add for &'a Complex

+ where P: Add, + Rhs: AsRef> +{ + type Output = Complex

; + fn add(self, rhs: Rhs) -> Self::Output { + let rhs = rhs.as_ref(); + Complex { re: self.re + rhs.re, im: self.im + rhs.im } + } +} + +use std::ops::Sub; + +impl Mul> for Complex + where T: Add + Sub + Mul + Copy +{ + type Output = Self; + fn mul(self, rhs: Self) -> Self { + Complex { re: self.re * rhs.re - self.im * rhs.im, + im: self.re * rhs.im + self.im * rhs.re } + } +} + +#[test] +fn test() { + let z = Complex { re: -2, im: 6 }; + let c = Complex { re: 1, im: 2 }; + assert_eq!(z + c, Complex { re: -1, im: 8 }); + assert_eq!(z * c, Complex { re: -14, im: 2 }); + assert_eq!(z.add(c), Complex { re: -1, im: 8 }); +} + +#[test] +fn test_explicit() { + use std::ops::Add; + + assert_eq!(4.125f32.add(5.75), 9.875); + assert_eq!(10.add(20), 10 + 20); +} + +impl Add> for f64 { + type Output = Complex; + fn add(self, rhs: Complex) -> Complex { + Complex { re: rhs.re + self, im: rhs.im } + } +} + +#[test] +fn add_complex_to_real() { + assert_eq!(30f64 + Complex { re: 10.0f64, im: 20.0 }, + Complex { re: 40.0, im: 20.0 }); +} + +use std::ops::Neg; + +impl Neg for Complex + where T: Neg +{ + type Output = Complex; + fn neg(self) -> Complex { + Complex { re: -self.re, im: -self.im } + } +} + +#[test] +fn negate_complex() { + let z = Complex { re: 3, im: 4 }; + assert_eq!(-z, Complex { re: -3, im: -4 }); +} + +use std::ops::AddAssign; + +impl AddAssign for Complex + where T: AddAssign +{ + fn add_assign(&mut self, rhs: Complex) { + self.re += rhs.re; + self.im += rhs.im; + } +} + +#[test] +fn compound_assignment() { + let mut z = Complex { re: 5, im: 6 }; + z += Complex { re: 7, im: 8 }; + assert_eq!(z, Complex { re: 12, im: 14 }); + + let mut title = "Love".to_string(); + title += ", Actually"; + assert_eq!(title, "Love, Actually"); +} + +impl PartialEq for Complex { + fn eq(&self, other: &Complex) -> bool { + self.re == other.re && self.im == other.im + } +} + +impl Eq for Complex { } + +#[test] +fn comparison() { + let x = Complex { re: 5, im: 2 }; + let y = Complex { re: 2, im: 5 }; + assert_eq!(x * y, Complex { re: 0, im: 29 }); +} + +use std::fmt; + +// To make the formatting examples mesh with the rest of this file, I've adapted +// them to work on the type `Complex`, where the book simply defines a new +// non-generic `Complex` type. The only changes are adding ``, and changing +// the field names. + +#[cfg(skip)] +impl fmt::Display for Complex { + fn fmt(&self, dest: &mut fmt::Formatter) -> fmt::Result { + let i_sign = if self.i < 0.0 { '-' } else { '+' }; + write!(dest, "{} {} {}i", self.r, i_sign, f64::abs(self.i)) + } +} + +impl fmt::Display for Complex { + fn fmt(&self, dest: &mut fmt::Formatter) -> fmt::Result { + let (r, i) = (self.re, self.im); + if dest.alternate() { + let abs = f64::sqrt(r * r + i * i); + let angle = f64::atan2(i, r) / std::f64::consts::PI * 180.0; + write!(dest, "{} ∠ {}°", abs, angle) + } else { + let i_sign = if i < 0.0 { '-' } else { '+' }; + write!(dest, "{} {} {}i", r, i_sign, f64::abs(i)) + } + } +} + +#[test] +fn custom_display_impl() { + let one_twenty = Complex { re: -0.5, im: 0.866 }; + assert_eq!(format!("{}", one_twenty), + "-0.5 + 0.866i"); + + let two_forty = Complex { re: -0.5, im: -0.866 }; + assert_eq!(format!("{}", two_forty), + "-0.5 - 0.866i"); + + let ninety = Complex { re: 0.0, im: 2.0 }; + assert_eq!(format!("{}", ninety), + "0 + 2i"); + assert_eq!(format!("{:#}", ninety), + "2 ∠ 90°"); +} diff --git a/copy/.gitignore b/copy/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/copy/.gitignore @@ -0,0 +1 @@ +target diff --git a/copy/Cargo.lock b/copy/Cargo.lock new file mode 100644 index 0000000..77c598d --- /dev/null +++ b/copy/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "copy" +version = "0.1.0" + diff --git a/copy/Cargo.toml b/copy/Cargo.toml new file mode 100644 index 0000000..bb0d869 --- /dev/null +++ b/copy/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "copy" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/copy/src/main.rs b/copy/src/main.rs new file mode 100644 index 0000000..867441f --- /dev/null +++ b/copy/src/main.rs @@ -0,0 +1,110 @@ +use std::fs; +use std::io; +use std::path::Path; + +/// Copy the existing directory `src` to the target path `dst`. +fn copy_dir_to(src: &Path, dst: &Path) -> io::Result<()> { + if !dst.is_dir() { + fs::create_dir(dst)?; + } + + for entry_result in src.read_dir()? { + let entry = entry_result?; + let file_type = entry.file_type()?; + copy_to(&entry.path(), &file_type, &dst.join(entry.file_name()))?; + } + + Ok(()) +} + +#[cfg(unix)] +use std::os::unix::fs::symlink; + +/// Stub implementation of `symlink` for platforms that don't provide it. +#[cfg(not(unix))] +fn symlink, Q: AsRef>(src: P, _dst: Q) -> std::io::Result<()> { + Err(io::Error::new(io::ErrorKind::Other, + format!("can't copy symbolic link: {}", + src.as_ref().display()))) +} + +/// Copy whatever is at `src` to the target path `dst`. +fn copy_to(src: &Path, src_type: &fs::FileType, dst: &Path) -> io::Result<()> { + if src_type.is_file() { + fs::copy(src, dst)?; + } else if src_type.is_dir() { + copy_dir_to(src, dst)?; + } else if src_type.is_symlink() { + let target = src.read_link()?; + symlink(target, dst)?; + } else { + return Err(io::Error::new(io::ErrorKind::Other, + format!("don't know how to copy: {}", + src.display()))); + } + Ok(()) +} + +fn copy_into(source: P, destination: Q) -> io::Result<()> + where P: AsRef, + Q: AsRef +{ + let src = source.as_ref(); + let dst = destination.as_ref(); + + match src.file_name() { + None => { + return Err(io::Error::new(io::ErrorKind::Other, + format!("can't copy nameless directory: {}", + src.display()))); + } + Some(src_name) => { + let md = src.metadata()?; + copy_to(src, &md.file_type(), &dst.join(src_name))?; + } + } + Ok(()) +} + +fn dwim_copy(source: P, destination: Q) -> io::Result<()> + where P: AsRef, + Q: AsRef +{ + let src = source.as_ref(); + let dst = destination.as_ref(); + + if dst.is_dir() { + copy_into(src, dst) + } else { + let md = src.metadata()?; + copy_to(src, &md.file_type(), dst) + } +} + +fn copy_main() -> io::Result<()> { + let args = std::env::args_os().collect::>(); + if args.len() < 3 { + println!("usage: copy FILE... DESTINATION"); + } else if args.len() == 3 { + dwim_copy(&args[1], &args[2])?; + } else { + let dst = Path::new(&args[args.len() - 1]); + if !dst.is_dir() { + return Err(io::Error::new(io::ErrorKind::Other, + format!("target '{}' is not a directory", + dst.display()))); + } + for i in 1 .. args.len() - 1 { + copy_into(&args[i], dst)?; + } + } + Ok(()) +} + +fn main() { + use std::io::Write; + + if let Err(err) = copy_main() { + writeln!(io::stderr(), "error: {}", err).unwrap(); + } +} diff --git a/echo-server/.gitignore b/echo-server/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/echo-server/.gitignore @@ -0,0 +1 @@ +target diff --git a/echo-server/Cargo.lock b/echo-server/Cargo.lock new file mode 100644 index 0000000..d0ad882 --- /dev/null +++ b/echo-server/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "echo-server" +version = "0.1.0" + diff --git a/echo-server/Cargo.toml b/echo-server/Cargo.toml new file mode 100644 index 0000000..99cfe3c --- /dev/null +++ b/echo-server/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "echo-server" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/echo-server/src/main.rs b/echo-server/src/main.rs new file mode 100644 index 0000000..c2366bf --- /dev/null +++ b/echo-server/src/main.rs @@ -0,0 +1,27 @@ +use std::net::TcpListener; +use std::io; +use std::thread::spawn; + +/// Accept connections forever, spawning a thread for each one. +fn echo_main(addr: &str) -> io::Result<()> { + let listener = TcpListener::bind(addr)?; + println!("listening on {}", addr); + loop { + // Wait for a client to connect. + let (mut stream, addr) = listener.accept()?; + println!("connection received from {}", addr); + + // Spawn a thread to handle this client. + let mut write_stream = stream.try_clone()?; + spawn(move || { + // Echo everything we receive from `stream` back to it. + io::copy(&mut stream, &mut write_stream) + .expect("error in client thread: "); + println!("connection closed"); + }); + } +} + +fn main() { + echo_main("127.0.0.1:17007").expect("error: "); +} diff --git a/fern_sim/.gitignore b/fern_sim/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/fern_sim/.gitignore @@ -0,0 +1 @@ +target diff --git a/fern_sim/Cargo.lock b/fern_sim/Cargo.lock new file mode 100644 index 0000000..53bbc51 --- /dev/null +++ b/fern_sim/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "fern_sim" +version = "0.1.0" + diff --git a/fern_sim/Cargo.toml b/fern_sim/Cargo.toml new file mode 100644 index 0000000..73e36a9 --- /dev/null +++ b/fern_sim/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "fern_sim" +version = "0.1.0" +authors = ["You "] +license = "MIT" +homepage = "https://fernsim.example.com/" +repository = "https://git.example.com/fernsim" +documentation = "http://fernsim.example.com/docs" +description = """ +Fern simulation, from the cellular level up. +""" + +[dependencies] diff --git a/fern_sim/src/cells.rs b/fern_sim/src/cells.rs new file mode 100644 index 0000000..3697d5d --- /dev/null +++ b/fern_sim/src/cells.rs @@ -0,0 +1,12 @@ +//! The simulation of biological cells, which is as low-level as we go. + +pub struct Cell { + x: f64, + y: f64 +} + +impl Cell { + pub fn distance_from_origin(&self) -> f64 { + f64::hypot(self.x, self.y) + } +} diff --git a/fern_sim/src/lib.rs b/fern_sim/src/lib.rs new file mode 100644 index 0000000..ad94c18 --- /dev/null +++ b/fern_sim/src/lib.rs @@ -0,0 +1,13 @@ +//! Simulate the growth of ferns, from the level of +//! individual cells on up. + +pub mod cells; +pub mod plant_structures; +pub mod simulation; +pub mod spores; + +pub use plant_structures::Fern; +pub use simulation::Terrarium; + +pub mod net; +pub use net::connect; diff --git a/fern_sim/src/net.rs b/fern_sim/src/net.rs new file mode 100644 index 0000000..397f119 --- /dev/null +++ b/fern_sim/src/net.rs @@ -0,0 +1,17 @@ +pub struct Session; + +pub fn connect() -> Session { + Session +} + +impl Session { + /// Upload all local terrariums to the online gallery. + /// + /// ```no_run + /// let mut session = fern_sim::connect(); + /// session.upload_all(); + /// ``` + pub fn upload_all(&mut self) { + unimplemented!(); + } +} diff --git a/fern_sim/src/plant_structures/leaves.rs b/fern_sim/src/plant_structures/leaves.rs new file mode 100644 index 0000000..50d63f5 --- /dev/null +++ b/fern_sim/src/plant_structures/leaves.rs @@ -0,0 +1,6 @@ +#![allow(dead_code)] +//! Simulation of individual leaves (for the formation of leaves, see `stem`). + +pub struct Leaf { + x: bool +} diff --git a/fern_sim/src/plant_structures/mod.rs b/fern_sim/src/plant_structures/mod.rs new file mode 100644 index 0000000..32ba8eb --- /dev/null +++ b/fern_sim/src/plant_structures/mod.rs @@ -0,0 +1,45 @@ +//! Higher-level biological structures. +//! +//! We always simulate a sample of all chemical interactions at the cellular +//! level, but simulating everything that way is just too computationally +//! expensive. Therefore we keep higher-level data structures representing +//! each fern's roots, leaves, and so on. When we simulate physics (light, air +//! currents, gravity) we always use these structures as shorthand for the +//! millions of cells they typically represent. On a more morbid note, these +//! structures stick around when stuff dies, so that dead fronds have weight, +//! cast shadows, and so on. + +// in plant_structures/mod.rs +pub mod roots; +pub mod stems; +pub mod leaves; + +pub use self::leaves::Leaf; +pub use self::roots::Root; + +use self::roots::RootSet; +use self::stems::StemSet; + +pub enum FernType { + Fiddlehead +} + +pub struct Fern { + pub roots: RootSet, + pub stems: StemSet +} + +impl Fern { + pub fn new(_type: FernType) -> Fern { + Fern { + roots: vec![], + stems: vec![stems::Stem { furled: true }] + } + } + + pub fn is_furled(&self) -> bool { !self.is_fully_unfurled() } + + pub fn is_fully_unfurled(&self) -> bool { + self.stems.iter().all(|s| !s.furled) + } +} diff --git a/fern_sim/src/plant_structures/roots.rs b/fern_sim/src/plant_structures/roots.rs new file mode 100644 index 0000000..d40b4f3 --- /dev/null +++ b/fern_sim/src/plant_structures/roots.rs @@ -0,0 +1,7 @@ +#![allow(dead_code)] + +pub struct Root { + x: bool +} + +pub type RootSet = Vec; diff --git a/fern_sim/src/plant_structures/stems.rs b/fern_sim/src/plant_structures/stems.rs new file mode 100644 index 0000000..70a8123 --- /dev/null +++ b/fern_sim/src/plant_structures/stems.rs @@ -0,0 +1,10 @@ +//! Stems hold the weight of the plant and are largely responsible for its +//! shape. Parameters on `Stem` (not `Leaf`) are responsible for pinnation, +//! the feathery leaf structure that's the most immediately recognizable +//! property of ferns. + +pub struct Stem { + pub furled: bool +} + +pub type StemSet = Vec; diff --git a/fern_sim/src/simulation.rs b/fern_sim/src/simulation.rs new file mode 100644 index 0000000..9fb16b6 --- /dev/null +++ b/fern_sim/src/simulation.rs @@ -0,0 +1,52 @@ +//! Overall simulation control. +//! +//! The simulation algorithm is complex and has a lot of tweakable parameters. + +use std::fs::File; +use std::time::Duration; +use plant_structures::{Fern, FernType}; + +/// The simulated universe. +pub struct Terrarium { + ferns: Vec +} + +impl Terrarium { + /// Create a new empty terrarium. + pub fn new() -> Terrarium { + Terrarium { ferns: vec![] } + } + + /// Load a terrarium from a `.tm` file. + pub fn load(filename: &str) -> Terrarium { + // This implementation is, like everything else in here, completely bogus + File::open(filename).unwrap(); // check that the file is there + Terrarium { + ferns: vec![ + Fern::new(FernType::Fiddlehead) + ] + } + } + + /// Get a reference to a fern inside the simulation. + pub fn fern(&self, index: usize) -> &Fern { + &self.ferns[index] + } + + #[allow(unused_variables)] + /// Let the sun shine in and run the simulation for a given + /// amount of time. + /// + /// # use fern_sim::Terrarium; + /// # use std::time::Duration; + /// # let mut tm = Terrarium::new(); + /// tm.apply_sunlight(Duration::from_secs(60)); + /// + pub fn apply_sunlight(&mut self, time: Duration) { + for f in &mut self.ferns { + for s in &mut f.stems { + s.furled = false; + } + } + } +} diff --git a/fern_sim/src/spores.rs b/fern_sim/src/spores.rs new file mode 100644 index 0000000..a6cfd91 --- /dev/null +++ b/fern_sim/src/spores.rs @@ -0,0 +1,28 @@ +#![allow(dead_code, unused_variables)] + +//! Fern reproduction. + +use cells::Cell; + +/// A cell made by an adult fern. It disperses on the wind as part of +/// the fern life cycle. A spore grows into a prothallus -- a whole +/// separate organism, up to 5mm across -- which produces a zygote, +/// which becomes a new fern. (Plant sex is complicated.) +pub struct Spore { + x: bool +} + +/// A compartment, usually on the bottom of a leaf, where spores form. +pub struct Sporangium { + x: bool +} + +/// Simulate the production of a spore by meiosis. +pub fn produce_spore(factory: &mut Sporangium) -> Spore { + Spore { x: false } +} + +/// Mix genes to prepare for meiosis (part of interphase). +fn recombine(parent: &mut Cell) { +} + diff --git a/fern_sim/tests/basic/empty.tm b/fern_sim/tests/basic/empty.tm new file mode 100644 index 0000000..6a20ce4 --- /dev/null +++ b/fern_sim/tests/basic/empty.tm @@ -0,0 +1 @@ +0 0 0 0 diff --git a/fern_sim/tests/unfurl.rs b/fern_sim/tests/unfurl.rs new file mode 100644 index 0000000..c60f078 --- /dev/null +++ b/fern_sim/tests/unfurl.rs @@ -0,0 +1,14 @@ +// tests/unfurl.rs - Fiddleheads unfurl in sunlight + +extern crate fern_sim; +use fern_sim::Terrarium; +use std::time::Duration; + +#[test] +fn test_fiddlehead_unfurling() { + let mut world = Terrarium::load("tests/unfurl_files/fiddlehead.tm"); + assert!(world.fern(0).is_furled()); + let one_hour = Duration::from_secs(60 * 60); + world.apply_sunlight(one_hour); + assert!(world.fern(0).is_fully_unfurled()); +} diff --git a/fern_sim/tests/unfurl_files/fiddlehead.tm b/fern_sim/tests/unfurl_files/fiddlehead.tm new file mode 100644 index 0000000..6a20ce4 --- /dev/null +++ b/fern_sim/tests/unfurl_files/fiddlehead.tm @@ -0,0 +1 @@ +0 0 0 0 diff --git a/gap-buffer/.gitignore b/gap-buffer/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/gap-buffer/.gitignore @@ -0,0 +1 @@ +target diff --git a/gap-buffer/Cargo.lock b/gap-buffer/Cargo.lock new file mode 100644 index 0000000..f954dec --- /dev/null +++ b/gap-buffer/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "gap-buffer" +version = "0.1.0" + diff --git a/gap-buffer/Cargo.toml b/gap-buffer/Cargo.toml new file mode 100644 index 0000000..160152b --- /dev/null +++ b/gap-buffer/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "gap-buffer" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/gap-buffer/src/lib.rs b/gap-buffer/src/lib.rs new file mode 100644 index 0000000..75e7640 --- /dev/null +++ b/gap-buffer/src/lib.rs @@ -0,0 +1,312 @@ +#![allow(dead_code)] + +mod gap { + use std; + use std::ops::Range; + + /// A GapBuffer is a sequence of elements of type `T` that can insert and + /// remove elements at any position in constant time. Indexing is also constant + /// time. However, changing the position at which insertion and removal occur + /// takes time proportional to the distance the insertion position is being + /// moved. + pub struct GapBuffer { + // Storage for elements. This has the capacity we need, but its length + // always remains zero. GapBuffer puts its elements and the gap in this + // `Vec`'s "unused" capacity. + storage: Vec, + + // Range of uninitialized elements in the middle of `storage`. + // Elements before and after this range are always initialized. + gap: Range + } + + impl GapBuffer { + pub fn new() -> GapBuffer { + GapBuffer { storage: Vec::new(), gap: 0..0 } + } + + /// Return the number of elements this GapBuffer could hold without + /// reallocation. + pub fn capacity(&self) -> usize { + self.storage.capacity() + } + + /// Return the number of elements this GapBuffer currently holds. + pub fn len(&self) -> usize { + self.capacity() - self.gap.len() + } + + /// Return the current insertion position. + pub fn position(&self) -> usize { + self.gap.start + } + + /// Return a pointer to the `index`'th element of the underlying storage, + /// as if the gap were not there. + /// + /// Safety: `index` must be less than self.capacity(). + unsafe fn space(&self, index: usize) -> *const T { + self.storage.as_ptr().offset(index as isize) + } + + /// Return a mutable pointer to the `index`'th element of the underlying + /// storage, as if the gap were not there. + /// + /// Safety: `index` must be less than self.capacity(). + unsafe fn space_mut(&mut self, index: usize) -> *mut T { + self.storage.as_mut_ptr().offset(index as isize) + } + + /// Return the offset in the buffer of the `index`'th element, taking + /// the gap into account. This does not check whether index is in range, + /// but it never returns the index of space in the gap. + fn index_to_raw(&self, index: usize) -> usize { + if index < self.gap.start { + index + } else { + index + self.gap.len() + } + } + + /// Return a reference to the `index`'th element, + /// or `None` if `index` is out of bounds. + pub fn get(&self, index: usize) -> Option<&T> { + let raw = self.index_to_raw(index); + if raw < self.capacity() { + unsafe { + // We just checked `raw` against self.capacity(), + // and index_to_raw skips the gap, so this is safe. + Some(&*self.space(raw)) + } + } else { + None + } + } + + /// Set the current insertion position to `pos`. + /// If `pos` is out of bounds, panic. + pub fn set_position(&mut self, pos: usize) { + if pos > self.len() { + panic!("index {} out of range for GapBuffer", pos); + } + + unsafe { + let gap = self.gap.clone(); + if pos > gap.start { + // `pos` falls after the gap. Move the gap right + // by shifting elements after the gap to before it. + let distance = pos - gap.start; + std::ptr::copy(self.space(gap.end), + self.space_mut(gap.start), + distance); + } else if pos < gap.start { + // `pos` falls before the gap. Move the gap left + // by shifting elements before the gap to after it. + let distance = gap.start - pos; + std::ptr::copy(self.space(pos), + self.space_mut(gap.end - distance), + distance); + } + + self.gap = pos .. pos + gap.len(); + } + } + + /// Insert `elt` at the current insertion position, + /// and leave the insertion position after it. + pub fn insert(&mut self, elt: T) { + if self.gap.len() == 0 { + self.enlarge_gap(); + } + + unsafe { + let index = self.gap.start; + std::ptr::write(self.space_mut(index), elt); + } + self.gap.start += 1; + } + + /// Insert the elements produced by `iter` at the current insertion + /// position, and leave the insertion position after them. + pub fn insert_iter(&mut self, iterable: I) + where I: IntoIterator + { + for item in iterable { + self.insert(item) + } + } + + /// Remove the element just after the insertion position + /// and return it, or return `None` if the insertion position + /// is at the end of the GapBuffer. + pub fn remove(&mut self) -> Option { + if self.gap.end == self.capacity() { + return None; + } + + let element = unsafe { + std::ptr::read(self.space(self.gap.end)) + }; + self.gap.end += 1; + Some(element) + } + + /// Double the capacity of `self.storage`. + fn enlarge_gap(&mut self) { + let mut new_capacity = self.capacity() * 2; + if new_capacity == 0 { + // The existing vector is empty. + // Choose a reasonable starting capacity. + new_capacity = 4; + } + + // We have no idea what resizing a Vec does with its "unused" + // capacity. So just create a new vector and move over the elements. + let mut new = Vec::with_capacity(new_capacity); + let after_gap = self.capacity() - self.gap.end; + let new_gap = self.gap.start .. new.capacity() - after_gap; + + unsafe { + // Move the elements that fall before the gap. + std::ptr::copy_nonoverlapping(self.space(0), + new.as_mut_ptr(), + self.gap.start); + + // Move the elements that fall after the gap. + let new_gap_end = new.as_mut_ptr().offset(new_gap.end as isize); + std::ptr::copy_nonoverlapping(self.space(self.gap.end), + new_gap_end, + after_gap); + } + + // This frees the old Vec, but drops no elements, + // because the Vec's length is zero. + self.storage = new; + self.gap = new_gap; + } + } + + impl Drop for GapBuffer { + fn drop(&mut self) { + unsafe { + for i in 0 .. self.gap.start { + std::ptr::drop_in_place(self.space_mut(i)); + } + for i in self.gap.end .. self.capacity() { + drop(std::ptr::read(self.space(i))); + } + } + } + } + + pub struct Iter<'a, T: 'a> { + buffer: &'a GapBuffer, + pos: usize + } + + impl<'a, T: 'a> Iterator for Iter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option<&'a T> { + if self.pos >= self.buffer.len() { + None + } else { + self.pos += 1; + self.buffer.get(self.pos - 1) + } + } + } + + impl<'a, T: 'a> IntoIterator for &'a GapBuffer { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + fn into_iter(self) -> Iter<'a, T> { + Iter { buffer: self, pos: 0 } + } + } + + impl GapBuffer { + pub fn get_string(&self) -> String { + let mut text = String::new(); + text.extend(self); + text + } + } + + use std::fmt; + impl fmt::Debug for GapBuffer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let indices = (0..self.gap.start).chain(self.gap.end .. self.capacity()); + let elements = indices.map(|i| unsafe { &*self.space(i) }); + f.debug_list().entries(elements).finish() + } + } +} + + +mod gap_tests { + #[test] + fn test() { + use gap::GapBuffer; + + let mut buf = GapBuffer::new(); + buf.insert_iter("Lord of the Rings".chars()); + buf.set_position(12); + + buf.insert_iter("Onion ".chars()); + + assert_eq!(buf.get_string(), "Lord of the Onion Rings"); + } + + #[test] + fn misc() { + use gap::GapBuffer; + + let mut gb = GapBuffer::new(); + println!("{:?}", gb); + gb.insert("foo".to_string()); + println!("{:?}", gb); + gb.insert("bar".to_string()); + println!("{:?}", gb); + gb.insert("baz".to_string()); + println!("{:?}", gb); + gb.insert("qux".to_string()); + println!("{:?}", gb); + gb.insert("quux".to_string()); + println!("{:?}", gb); + + gb.set_position(2); + + assert_eq!(gb.remove(), Some("baz".to_string())); + println!("{:?}", gb); + assert_eq!(gb.remove(), Some("qux".to_string())); + println!("{:?}", gb); + assert_eq!(gb.remove(), Some("quux".to_string())); + println!("{:?}", gb); + assert_eq!(gb.remove(), None); + println!("{:?}", gb); + + gb.insert("quuux".to_string()); + println!("{:?}", gb); + + gb.set_position(0); + assert_eq!(gb.remove(), Some("foo".to_string())); + println!("{:?}", gb); + assert_eq!(gb.remove(), Some("bar".to_string())); + println!("{:?}", gb); + assert_eq!(gb.remove(), Some("quuux".to_string())); + println!("{:?}", gb); + assert_eq!(gb.remove(), None); + println!("{:?}", gb); + } + + #[test] + fn drop_elements() { + use gap::GapBuffer; + + let mut gb = GapBuffer::new(); + gb.insert("foo".to_string()); + gb.insert("bar".to_string()); + + gb.set_position(1); + } +} diff --git a/gcd/.gitignore b/gcd/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/gcd/.gitignore @@ -0,0 +1 @@ +target diff --git a/gcd/Cargo.lock b/gcd/Cargo.lock new file mode 100644 index 0000000..03a3077 --- /dev/null +++ b/gcd/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "gcd" +version = "0.1.0" + diff --git a/gcd/Cargo.toml b/gcd/Cargo.toml new file mode 100644 index 0000000..da22fe7 --- /dev/null +++ b/gcd/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "gcd" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/gcd/src/main.rs b/gcd/src/main.rs new file mode 100644 index 0000000..83cc0f0 --- /dev/null +++ b/gcd/src/main.rs @@ -0,0 +1,46 @@ +fn gcd(mut n: u64, mut m: u64) -> u64 { + assert!(n != 0 && m != 0); + while m != 0 { + if m < n { + let t = m; + m = n; + n = t; + } + m = m % n; + } + n +} + +#[test] +fn test_gcd() { + assert_eq!(gcd(14, 15), 1); + + assert_eq!(gcd(2 * 3 * 5 * 11 * 17, + 3 * 7 * 11 * 13 * 19), + 3 * 11); +} + +use std::io::Write; +use std::str::FromStr; + +fn main() { + let mut numbers = Vec::new(); + + for arg in std::env::args().skip(1) { + numbers.push(u64::from_str(&arg) + .expect("error parsing argument")); + } + + if numbers.len() == 0 { + writeln!(std::io::stderr(), "Usage: gcd NUMBER ...").unwrap(); + std::process::exit(1); + } + + let mut d = numbers[0]; + for m in &numbers[1..] { + d = gcd(d, *m); + } + + println!("The greatest common divisor of {:?} is {}", + numbers, d); +} diff --git a/generic-queue/.gitignore b/generic-queue/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/generic-queue/.gitignore @@ -0,0 +1 @@ +target diff --git a/generic-queue/Cargo.lock b/generic-queue/Cargo.lock new file mode 100644 index 0000000..c0f3b06 --- /dev/null +++ b/generic-queue/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "generic-queue" +version = "0.1.0" + diff --git a/generic-queue/Cargo.toml b/generic-queue/Cargo.toml new file mode 100644 index 0000000..53c2ebf --- /dev/null +++ b/generic-queue/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "generic-queue" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/generic-queue/src/lib.rs b/generic-queue/src/lib.rs new file mode 100644 index 0000000..d8ae4d6 --- /dev/null +++ b/generic-queue/src/lib.rs @@ -0,0 +1,90 @@ +pub struct Queue { + older: Vec, + younger: Vec +} + +impl Queue { + pub fn new() -> Queue { + Queue { older: Vec::new(), younger: Vec::new() } + } + + pub fn push(&mut self, t: T) { + self.younger.push(t); + } + + pub fn is_empty(&self) -> bool { + self.older.is_empty() && self.younger.is_empty() + } + + pub fn pop(&mut self) -> Option { + if self.older.is_empty() { + use std::mem::swap; + + if self.younger.is_empty() { + return None; + } + + // Bring the elements in younger over to older, and put them in + // the promised order. + swap(&mut self.older, &mut self.younger); + self.older.reverse(); + } + + // Now older is guaranteed to have something. Vec's pop method + // already returns an Option, so we're set. + self.older.pop() + } + + pub fn split(self) -> (Vec, Vec) { + (self.older, self.younger) + } +} + +#[test] +fn test() { + let mut q = Queue::new(); + + q.push('*'); + assert_eq!(q.pop(), Some('*')); + assert_eq!(q.pop(), None); + + q.push('0'); + q.push('1'); + assert_eq!(q.pop(), Some('0')); + + q.push('∞'); + assert_eq!(q.pop(), Some('1')); + assert_eq!(q.pop(), Some('∞')); + assert_eq!(q.pop(), None); + + assert!(q.is_empty()); + q.push('☉'); + assert!(!q.is_empty()); + q.pop(); + assert!(q.is_empty()); + + let mut q = Queue::new(); + + q.push('P'); + q.push('D'); + assert_eq!(q.pop(), Some('P')); + q.push('X'); + + assert_eq!(q.split(), (vec!['D'], vec!['X'])); +} + +#[test] +fn test_generic() { + let mut q = Queue::::new(); + &mut q; + drop(q); + + let mut q = Queue::new(); + let mut r = Queue::new(); + + q.push("CAD"); // apparently a Queue<&'static str> + r.push(0.74); // apparently a Queue + + q.push("BTC"); // Bitcoins per USD, 2017-5 + r.push(2737.7); // Rust fails to detect irrational exuberance +} diff --git a/grep/.gitignore b/grep/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/grep/.gitignore @@ -0,0 +1 @@ +target diff --git a/grep/Cargo.lock b/grep/Cargo.lock new file mode 100644 index 0000000..ca1c600 --- /dev/null +++ b/grep/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "grep" +version = "0.1.0" + diff --git a/grep/Cargo.toml b/grep/Cargo.toml new file mode 100644 index 0000000..14dc747 --- /dev/null +++ b/grep/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "grep" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/grep/src/main.rs b/grep/src/main.rs new file mode 100644 index 0000000..e6a128a --- /dev/null +++ b/grep/src/main.rs @@ -0,0 +1,49 @@ +// grep - Search stdin or some files for lines matching a given string. + +use std::error::Error; +use std::io::{self, BufReader}; +use std::io::prelude::*; +use std::fs::File; +use std::path::PathBuf; + +fn grep(target: &str, reader: R) -> io::Result<()> + where R: BufRead +{ + for line_result in reader.lines() { + let line = line_result?; + if line.contains(target) { + println!("{}", line); + } + } + Ok(()) +} + +fn grep_main() -> Result<(), Box> { + // Get the command-line arguments. The first argument is the + // string to search for; the rest are filenames. + let mut args = std::env::args().skip(1); + let target = match args.next() { + Some(s) => s, + None => Err("usage: grep PATTERN FILE...")? + }; + let files: Vec = args.map(PathBuf::from).collect(); + + if files.is_empty() { + let stdin = io::stdin(); + grep(&target, stdin.lock())?; + } else { + for file in files { + let f = File::open(file)?; + grep(&target, BufReader::new(f))?; + } + } + + Ok(()) +} + +fn main() { + let result = grep_main(); + if let Err(err) = result { + let _ = writeln!(io::stderr(), "{}", err); + } +} diff --git a/http-get/.gitignore b/http-get/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/http-get/.gitignore @@ -0,0 +1 @@ +target diff --git a/http-get/Cargo.lock b/http-get/Cargo.lock new file mode 100644 index 0000000..5b6af62 --- /dev/null +++ b/http-get/Cargo.lock @@ -0,0 +1,476 @@ +[[package]] +name = "advapi32-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "antidote" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "core-foundation" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crypt32-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dtoa" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foreign-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gcc" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gdi32-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "http-get" +version = "0.1.0" +dependencies = [ + "reqwest 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-native-tls" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.7 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libflate" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "matches" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mime" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "openssl 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", + "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "reqwest" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hyper 0.10.7 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "schannel" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "secur32-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "0.9.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_json" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_urlencoded" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempdir" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "typeable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicase" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "url" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "user32-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a" +"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" +"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" +"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" +"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" +"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" +"checksum crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec" +"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" +"checksum foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d" +"checksum gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "40899336fb50db0c78710f53e87afc54d8c7266fb76262fecc78ca1a7f09deae" +"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" +"checksum httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e7a63e511f9edffbab707141fbb8707d1a3098615fb2adbd5769cdfcc9b17d" +"checksum hyper 0.10.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb15973f070a7de8bf3f61261726ef09647741110af6062d5ca1e21e6c4bd013" +"checksum hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "afe68f772f0497a7205e751626bb8e1718568b58534b6108c73a74ef80483409" +"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11" +"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" +"checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" +"checksum libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a51822fc847e7a8101514d1d44e354ba2ffa7d4c194dcab48870740e327cac70" +"checksum libflate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "202cf589fbdf78809ae1bb7fd5d1da2627136c966e756127cd14fa00c7e2da41" +"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" +"checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1" +"checksum mime 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5c93a4bd787ddc6e7833c519b73a50883deb5863d76d9b71eb8216fb7f94e66" +"checksum native-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e94a2fc65a44729fe969cc973da87c1052ae3f000b2cb33029f14aeb85550d5" +"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99" +"checksum num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "55aabf4e2d6271a2e4e4c0f2ea1f5b07cc589cc1a9e9213013b54a76678ca4f3" +"checksum openssl 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8aa0eb7aad44f0da6f7dda13ddb4559d91a0f40cfab150b1f76ad5b39ec523f" +"checksum openssl-sys 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "14f5bfd12054d764510b887152d564ba11d99ae24ea7d740781778f646620576" +"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" +"checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" +"checksum reqwest 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5113a267e410c17d186189940711e49da445987299763f63503cfed4dbbcccc0" +"checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b" +"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" +"checksum schannel 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b291854e37196c2b67249e09d6bdeff410b19e1acf05558168e9c4413b4e95" +"checksum secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f412dfa83308d893101dd59c10d6fda8283465976c28c287c5c855bf8d216bc" +"checksum security-framework 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "42ddf098d78d0b64564b23ee6345d07573e7d10e52ad86875d89ddf5f8378a02" +"checksum security-framework-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "5bacdada57ea62022500c457c8571c17dfb5e6240b7c8eac5916ffa8c7138a55" +"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" +"checksum serde 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)" = "231dfd55909400769e437326cfb4af8bec97c3dd56ab3d02df8ef5c7e00f179b" +"checksum serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ad8bcf487be7d2e15d3d543f04312de991d631cfe1b43ea0ade69e6a8a5b16a1" +"checksum serde_urlencoded 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4cde9c1d41c4852426d097c53b9151c53e314e9c6ec8a7765e083137d45c76" +"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" +"checksum time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7ec6d62a20df54e07ab3b78b9a3932972f4b7981de295563686849eb3989af" +"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" +"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" +"checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764" +"checksum unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c1f7ceb96afdfeedee42bade65a0d585a6a0106f681b6749c8ff4daa8df30b3f" +"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172" +"checksum url 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f024e241a55f5c88401595adc1d4af0c9649e91da82d0e190fe55950231ae575" +"checksum user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ef4711d107b21b410a3a974b1204d9accc8b10dad75d8324b5d755de1617d47" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/http-get/Cargo.toml b/http-get/Cargo.toml new file mode 100644 index 0000000..4a345c6 --- /dev/null +++ b/http-get/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "http-get" +version = "0.1.0" +authors = ["You "] + +[dependencies] +reqwest = "0.5.1" diff --git a/http-get/src/main.rs b/http-get/src/main.rs new file mode 100644 index 0000000..ffc43f8 --- /dev/null +++ b/http-get/src/main.rs @@ -0,0 +1,30 @@ +extern crate reqwest; + +use std::error::Error; +use std::io::{self, Write}; + +fn http_get_main(url: &str) -> Result<(), Box> { + // Send the HTTP request and get a response. + let mut response = reqwest::get(url)?; + if !response.status().is_success() { + Err(format!("{}", response.status()))?; + } + + // Read the response body and write it to stdout. + let stdout = io::stdout(); + io::copy(&mut response, &mut stdout.lock())?; + + Ok(()) +} + +fn main() { + let args: Vec = std::env::args().collect(); + if args.len() != 2 { + writeln!(io::stderr(), "usage: http-get URL").unwrap(); + return; + } + + if let Err(err) = http_get_main(&args[1]) { + writeln!(io::stderr(), "error: {}", err).unwrap(); + } +} diff --git a/interval/.gitignore b/interval/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/interval/.gitignore @@ -0,0 +1 @@ +target diff --git a/interval/Cargo.lock b/interval/Cargo.lock new file mode 100644 index 0000000..f5917d4 --- /dev/null +++ b/interval/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "interval" +version = "0.1.0" + diff --git a/interval/Cargo.toml b/interval/Cargo.toml new file mode 100644 index 0000000..e554b6d --- /dev/null +++ b/interval/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "interval" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/interval/src/lib.rs b/interval/src/lib.rs new file mode 100644 index 0000000..2a122e9 --- /dev/null +++ b/interval/src/lib.rs @@ -0,0 +1,30 @@ +#[derive(Debug, PartialEq)] +struct Interval { + lower: T, // inclusive + upper: T // exclusive +} + +use std::cmp::{Ordering, PartialOrd}; + +impl PartialOrd> for Interval { + fn partial_cmp(&self, other: &Interval) -> Option { + if self == other { Some(Ordering::Equal) } + else if self.lower >= other.upper { Some(Ordering::Greater) } + else if self.upper <= other.lower { Some(Ordering::Less) } + else { None } + } +} + +#[test] +fn test() { + assert!(Interval { lower: 10, upper: 20 } < Interval { lower: 20, upper: 40 }); + assert!(Interval { lower: 7, upper: 8 } >= Interval { lower: 0, upper: 1 }); + assert!(Interval { lower: 7, upper: 8 } <= Interval { lower: 7, upper: 8 }); + assert!(Interval { lower: 7, upper: 8 }.le(&Interval { lower: 7, upper: 8 })); + + // Overlapping intervals aren't ordered with respect to each other. + let left = Interval { lower: 10, upper: 30 }; + let right = Interval { lower: 20, upper: 40 }; + assert!(!(left < right)); + assert!(!(left >= right)); +} diff --git a/iron-gcd/.gitignore b/iron-gcd/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/iron-gcd/.gitignore @@ -0,0 +1 @@ +target diff --git a/iron-gcd/Cargo.lock b/iron-gcd/Cargo.lock new file mode 100644 index 0000000..38cf894 --- /dev/null +++ b/iron-gcd/Cargo.lock @@ -0,0 +1,382 @@ +[[package]] +name = "base64" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bodyparser" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "persistent 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "conduit-mime-types" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dtoa" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "error" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.10.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iron" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iron-gcd" +version = "0.1.0" +dependencies = [ + "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "router 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "urlencoded 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "matches" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mime" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "modifier" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-traits" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "persistent" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "plugin" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "route-recognizer" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "router" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "route-recognizer 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "safemem" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_json" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "typeable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "typemap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicase" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unsafe-any" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "urlencoded" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bodyparser 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" +"checksum bodyparser 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6928e817538b74a73d1dd6e9a942a2a35c632a597b6bb14fd009480f859a6bf5" +"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" +"checksum conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95ca30253581af809925ef68c2641cc140d6183f43e12e0af4992d53768bd7b8" +"checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d" +"checksum error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e606f14042bb87cc02ef6a14db6c90ab92ed6f62d87e69377bc759fd7987cc" +"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07" +"checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" +"checksum idna 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2233d4940b1f19f0418c158509cd7396b8d70a5db5705ce410914dc8fa603b37" +"checksum iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2440ae846e7a8c7f9b401db8f6e31b4ea5e7d3688b91761337da7e054520c75b" +"checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" +"checksum lazy_static 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "236eb37a62591d4a41a89b7763d7de3e06ca02d5ab2815446a8bae5d2f8c2d57" +"checksum libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "e7eb6b826bfc1fdea7935d46556250d1799b7fe2d9f7951071f4291710665e3e" +"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" +"checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1" +"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" +"checksum modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58" +"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99" +"checksum num_cpus 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6e850c7f35c3de263e6094e819f6b4b9c09190ff4438fc6dec1aef1568547bc" +"checksum persistent 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c9c94f2ef72dc272c6bcc8157ccf2bc7da14f4c58c69059ac2fc48492d6916" +"checksum plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0" +"checksum redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "29dbdfd4b9df8ab31dec47c6087b7b13cbf4a776f335e4de8efba8288dda075b" +"checksum route-recognizer 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3255338088df8146ba63d60a9b8e3556f1146ce2973bc05a75181a42ce2256" +"checksum router 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9b1797ff166029cb632237bb5542696e54961b4cf75a324c6f05c9cf0584e4e" +"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" +"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" +"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" +"checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" +"checksum serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "67f7d2e9edc3523a9c8ec8cd6ec481b3a27810aafee3e625d311febd3e656b4c" +"checksum time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd7ccbf969a892bf83f1e441126968a07a3941c24ff522a26af9f9f4585d1a3" +"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" +"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" +"checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" +"checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764" +"checksum unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a6a2c4e3710edd365cd7e78383153ed739fa31af19f9172f72d3575060f5a43a" +"checksum unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e28fa37426fceeb5cf8f41ee273faa7c82c47dc8fba5853402841e665fcd86ff" +"checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" +"checksum url 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2ba3456fbe5c0098cb877cf08b92b76c3e18e0be9e47c35b487220d377d24e" +"checksum urlencoded 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c28708636d6f7298a53b1cdb6af40f1ab523209a7cb83cf4d41b3ebc671d319" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/iron-gcd/Cargo.toml b/iron-gcd/Cargo.toml new file mode 100644 index 0000000..5728e96 --- /dev/null +++ b/iron-gcd/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "iron-gcd" +version = "0.1.0" +authors = ["You "] + +[dependencies] +iron = "0.5.1" +mime = "0.2.3" +router = "0.5.1" +urlencoded = "0.5.0" diff --git a/iron-gcd/README.md b/iron-gcd/README.md new file mode 100644 index 0000000..5276bd8 --- /dev/null +++ b/iron-gcd/README.md @@ -0,0 +1,5 @@ +# A simple web server, in Rust + +This program uses the `Iron` framework to implement a simple web server in Rust. +Run the program with `cargo run`, visit `http://localhost:3000/`, and then ask +it to compute the greatest common denominator of some numbers for you! diff --git a/iron-gcd/src/main.rs b/iron-gcd/src/main.rs new file mode 100644 index 0000000..b792d50 --- /dev/null +++ b/iron-gcd/src/main.rs @@ -0,0 +1,101 @@ +extern crate iron; +#[macro_use] extern crate mime; + +use iron::prelude::*; +use iron::status; + +extern crate router; +use router::Router; + +fn main() { + let mut router = Router::new(); + + router.get("/", get_form, "root"); + router.post("/gcd", post_gcd, "gcd"); + + println!("Serving on http://localhost:3000..."); + Iron::new(router).http("localhost:3000").unwrap(); +} + +fn get_form(_request: &mut Request) -> IronResult { + let mut response = Response::new(); + + response.set_mut(status::Ok); + response.set_mut(mime!(Text/Html; Charset=Utf8)); + response.set_mut(r#" + GCD Calculator + + + + + + "#); + + Ok(response) +} + +extern crate urlencoded; + +use std::str::FromStr; +use urlencoded::UrlEncodedBody; + +fn post_gcd(request: &mut Request) -> IronResult { + let mut response = Response::new(); + + let form_data = match request.get_ref::() { + Err(e) => { + response.set_mut(status::BadRequest); + response.set_mut(format!("Error parsing form data: {:?}\n", e)); + return Ok(response); + } + Ok(map) => map + }; + + let unparsed_numbers = match form_data.get("n") { + None => { + response.set_mut(status::BadRequest); + response.set_mut(format!("form data has no 'n' parameter\n")); + return Ok(response); + } + Some(nums) => nums + }; + + let mut numbers = Vec::new(); + for unparsed in unparsed_numbers { + match u64::from_str(&unparsed) { + Err(_) => { + response.set_mut(status::BadRequest); + response.set_mut( + format!("Value for 'n' parameter not a number: {:?}\n", + unparsed)); + return Ok(response); + } + Ok(n) => { numbers.push(n); } + } + } + + let mut d = numbers[0]; + for m in &numbers[1..] { + d = gcd(d, *m); + } + + response.set_mut(status::Ok); + response.set_mut(mime!(Text/Html; Charset=Utf8)); + response.set_mut( + format!("The greatest common divisor of the numbers {:?} is {}\n", + numbers, d)); + Ok(response) +} + +fn gcd(mut n: u64, mut m: u64) -> u64 { + assert!(n != 0 && m != 0); + while m != 0 { + if m < n { + let t = m; + m = n; + n = t; + } + m = m % n; + } + n +} diff --git a/json-macro/.gitignore b/json-macro/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/json-macro/.gitignore @@ -0,0 +1 @@ +target diff --git a/json-macro/Cargo.lock b/json-macro/Cargo.lock new file mode 100644 index 0000000..29f9de6 --- /dev/null +++ b/json-macro/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "json-macro" +version = "0.1.0" + diff --git a/json-macro/Cargo.toml b/json-macro/Cargo.toml new file mode 100644 index 0000000..2ba7946 --- /dev/null +++ b/json-macro/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "json-macro" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/json-macro/src/lib.rs b/json-macro/src/lib.rs new file mode 100644 index 0000000..3f26d74 --- /dev/null +++ b/json-macro/src/lib.rs @@ -0,0 +1,45 @@ +use std::collections::HashMap; + +#[macro_use] mod macros; + +#[derive(Clone, PartialEq, Debug)] +pub enum Json { + Null, + Boolean(bool), + Number(f64), + String(String), + Array(Vec), + Object(Box>) +} + +impl From for Json { + fn from(b: bool) -> Json { + Json::Boolean(b) + } +} + +impl From for Json { + fn from(s: String) -> Json { + Json::String(s) + } +} + +impl<'a> From<&'a str> for Json { + fn from(s: &'a str) -> Json { + Json::String(s.to_string()) + } +} + +macro_rules! impl_from_num_for_json { + ( $( $t:ident )* ) => { + $( + impl From<$t> for Json { + fn from(n: $t) -> Json { + Json::Number(n as f64) + } + } + )* + }; +} + +impl_from_num_for_json!(u8 i8 u16 i16 u32 i32 u64 i64 usize isize f32 f64); diff --git a/json-macro/src/macros.rs b/json-macro/src/macros.rs new file mode 100644 index 0000000..7752002 --- /dev/null +++ b/json-macro/src/macros.rs @@ -0,0 +1,145 @@ +// macros.rs +pub use std::collections::HashMap; +pub use std::boxed::Box; +pub use std::string::ToString; + +#[macro_export] +macro_rules! json { + (null) => { + $crate::Json::Null + }; + ([ $( $element:tt ),* ]) => { + $crate::Json::Array(vec![ $( json!($element) ),* ]) + }; + ({ $( $key:tt : $value:tt ),* }) => { + { + let mut fields = $crate::macros::Box::new( + $crate::macros::HashMap::new()); + $( fields.insert($crate::macros::ToString::to_string($key), + json!($value)); )* + $crate::Json::Object(fields) + } + }; + ($other:tt) => { + $crate::Json::from($other) + }; +} + + + +#[cfg(test)] +mod tests { + use ::Json; + + #[test] + fn json_with_rust_expressions() { + const HELLO: &'static str = "hello"; + let macro_generated_value = + json!({ + "math_works": (4 - 2 == 2), + "en": HELLO, + HELLO: "bonjour!" + }) + ; + let hand_coded_value = Json::Object(Box::new(vec![ + ("math_works".to_string(), Json::Boolean(true)), + ("en".to_string(), Json::String("hello".to_string())), + ("hello".to_string(), Json::String("bonjour!".to_string())), + ].into_iter().collect())); + assert_eq!(macro_generated_value, hand_coded_value); + } + + // Tests from earlier in the chapter should actually pass with this macro. + + #[test] + fn original_example() { + let hand_coded_value = { + let students = Json::Array(vec![ + Json::Object(Box::new(vec![ + ("name".to_string(), Json::String("Jim Blandy".to_string())), + ("class_of".to_string(), Json::Number(1926.0)), + ("major".to_string(), Json::String("Tibetan throat singing".to_string())) + ].into_iter().collect())), + Json::Object(Box::new(vec![ + ("name".to_string(), Json::String("Jason Orendorff".to_string())), + ("class_of".to_string(), Json::Number(1702.0)), + ("major".to_string(), Json::String("Knots".to_string())) + ].into_iter().collect())) + ]); + students + }; + + let macro_generated_value = { + let students = json!([ + { + "name": "Jim Blandy", + "class_of": 1926, + "major": "Tibetan throat singing" + }, + { + "name": "Jason Orendorff", + "class_of": 1702, + "major": "Knots" + } + ]); + students + }; + + assert_eq!(macro_generated_value, hand_coded_value); + } + + #[test] + fn json_array_with_json_element() { + let macro_generated_value = json!( + [ + // valid JSON that doesn't match `$element:expr` + { + "pitch": 440.0 + } + ] + ); + let hand_coded_value = + Json::Array(vec![ + Json::Object(Box::new(vec![ + ("pitch".to_string(), Json::Number(440.0)) + ].into_iter().collect())) + ]); + assert_eq!(macro_generated_value, hand_coded_value); + } + + #[test] + fn json_monolith() { + let width = 4.0; + let desc = + json!({ + "width": width, + "height": (width * 9.0 / 4.0) + }); + + let hand_coded_value = + Json::Object(Box::new(vec![ + ("width".to_string(), Json::Number(width)), + ("height".to_string(), Json::Number(width * 9.0 / 4.0)) + ].into_iter().collect())); + assert_eq!(desc, hand_coded_value); + } + + #[test] + fn hygiene() { + // The surprise is that *the macro works as-is*. + // Rust renames the variable for you! + + let fields = "Fields, W.C."; + let role = json!({ + "name": "Larson E. Whipsnade", + "actor": fields + }); + + let hand_coded_value = + Json::Object(Box::new(vec![ + ("name".to_string(), Json::String("Larson E. Whipsnade".to_string())), + ("actor".to_string(), Json::String("Fields, W.C.".to_string())) + ].into_iter().collect())); + assert_eq!(role, hand_coded_value); + } +} diff --git a/libgit2-rs-safe/.gitignore b/libgit2-rs-safe/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/libgit2-rs-safe/.gitignore @@ -0,0 +1 @@ +target diff --git a/libgit2-rs-safe/Cargo.lock b/libgit2-rs-safe/Cargo.lock new file mode 100644 index 0000000..288d8e8 --- /dev/null +++ b/libgit2-rs-safe/Cargo.lock @@ -0,0 +1,14 @@ +[[package]] +name = "git-toy" +version = "0.1.0" +dependencies = [ + "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "e7eb6b826bfc1fdea7935d46556250d1799b7fe2d9f7951071f4291710665e3e" diff --git a/libgit2-rs-safe/Cargo.toml b/libgit2-rs-safe/Cargo.toml new file mode 100644 index 0000000..98ddf5c --- /dev/null +++ b/libgit2-rs-safe/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "git-toy" +version = "0.1.0" +authors = ["You "] +build = "build.rs" + +[dependencies] +libc = "0.2.23" diff --git a/libgit2-rs-safe/build.rs b/libgit2-rs-safe/build.rs new file mode 100644 index 0000000..ee72958 --- /dev/null +++ b/libgit2-rs-safe/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-search=native=/home/jimb/libgit2-0.25.1/build"); +} diff --git a/libgit2-rs-safe/src/git/mod.rs b/libgit2-rs-safe/src/git/mod.rs new file mode 100644 index 0000000..3fd647b --- /dev/null +++ b/libgit2-rs-safe/src/git/mod.rs @@ -0,0 +1,251 @@ +mod raw; + +use std::error; +use std::fmt; +use std::result; + +#[derive(Debug)] +pub struct Error { + code: i32, + message: String, + class: i32 +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + // Displaying an `Error` simply displays the message from libgit2. + self.message.fmt(f) + } +} + +impl error::Error for Error { + fn description(&self) -> &str { &self.message } +} + +pub type Result = result::Result; + +use std::os::raw::c_int; +use std::ffi::CStr; + +fn check(code: c_int) -> Result { + if code >= 0 { + return Ok(code); + } + + unsafe { + let error = raw::giterr_last(); + + // libgit2 ensures that (*error).message is always non-null and null + // terminated, so this call is safe. + let message = CStr::from_ptr((*error).message) + .to_string_lossy() + .into_owned(); + + Err(Error { + code: code as i32, + message, + class: (*error).klass as i32 + }) + } +} + +/// A Git repository. +pub struct Repository { + // This must always be a pointer to a live `git_repository` structure, + // No other `Repository` may point to it. + raw: *mut raw::git_repository +} + +use std::path::Path; + +impl Repository { + pub fn open>(path: P) -> Result { + ensure_initialized(); + + let path = path_to_cstring(path.as_ref())?; + let mut repo = null_mut(); + unsafe { + check(raw::git_repository_open(&mut repo, path.as_ptr()))?; + } + Ok(Repository { raw: repo }) + } +} + +use std; +use libc; + +fn ensure_initialized() { + static ONCE: std::sync::Once = std::sync::ONCE_INIT; + ONCE.call_once(|| { + unsafe { + check(raw::git_libgit2_init()) + .expect("initializing libgit2 failed"); + assert_eq!(libc::atexit(shutdown), 0); + } + }); +} + +use std::io::Write; + +extern fn shutdown() { + unsafe { + if let Err(e) = check(raw::git_libgit2_shutdown()) { + let _ = writeln!(std::io::stderr(), + "shutting down libgit2 failed: {}", + e); + std::process::abort(); + } + } +} + +impl Drop for Repository { + fn drop(&mut self) { + unsafe { + raw::git_repository_free(self.raw); + } + } +} + +use std::ffi::CString; + +#[cfg(unix)] +fn path_to_cstring(path: &Path) -> Result { + // The `as_bytes` method exists only on Unix-like systems. + use std::os::unix::ffi::OsStrExt; + + Ok(CString::new(path.as_os_str().as_bytes())?) +} + +#[cfg(windows)] +fn path_to_cstring(path: &Path) -> Result { + // Try to convert to UTF-8. If this fails, libgit2 can't handle the path + // anyway. + match path.to_str() { + Some(s) => Ok(CString::new(s)?), + None => { + let message = format!("Couldn't convert path '{}' to UTF-8", + path.display()); + Err(message.into()) + } + } +} + +impl From for Error { + fn from(message: String) -> Error { + Error { code: -1, message, class: 0 } + } +} + +// NulError is what `CString::new` returns if a string +// has embedded zero bytes. +impl From for Error { + fn from(e: std::ffi::NulError) -> Error { + Error { code: -1, message: e.to_string(), class: 0 } + } +} + +/// The identifier of some sort of object stored in the Git object +/// database: a commit, tree, blob, tag, etc. This is a wide hash of the +/// object's contents. +pub struct Oid { + pub raw: raw::git_oid +} + +use std::mem::uninitialized; +use std::os::raw::c_char; + +impl Repository { + pub fn reference_name_to_id(&self, name: &str) -> Result { + let name = CString::new(name)?; + unsafe { + let mut oid = uninitialized(); + check(raw::git_reference_name_to_id(&mut oid, self.raw, + name.as_ptr() as *const c_char))?; + Ok(Oid { raw: oid }) + } + } +} + +use std::marker::PhantomData; + +pub struct Commit<'repo> { + // This must always be a pointer to a usable `git_commit` structure. + raw: *mut raw::git_commit, + _marker: PhantomData<&'repo Repository> +} + +use std::ptr::null_mut; + +impl Repository { + pub fn find_commit(&self, oid: &Oid) -> Result { + let mut commit = null_mut(); + unsafe { + check(raw::git_commit_lookup(&mut commit, self.raw, &oid.raw))?; + } + Ok(Commit { raw: commit, _marker: PhantomData }) + } +} + +impl<'repo> Drop for Commit<'repo> { + fn drop(&mut self) { + unsafe { + raw::git_commit_free(self.raw); + } + } +} + +impl<'repo> Commit<'repo> { + pub fn author(&self) -> Signature { + unsafe { + Signature { + raw: raw::git_commit_author(self.raw), + _marker: PhantomData + } + } + } + + pub fn message(&self) -> Option<&str> { + unsafe { + let message = raw::git_commit_message(self.raw); + char_ptr_to_str(self, message) + } + } +} + +pub struct Signature<'text> { + raw: *const raw::git_signature, + _marker: PhantomData<&'text str> +} + +impl<'text> Signature<'text> { + /// Return the author's name as a `&str`, + /// or `None` if it is not well-formed UTF-8. + pub fn name(&self) -> Option<&str> { + unsafe { + char_ptr_to_str(self, (*self.raw).name) + } + } + + /// Return the author's email as a `&str`, + /// or `None` if it is not well-formed UTF-8. + pub fn email(&self) -> Option<&str> { + unsafe { + char_ptr_to_str(self, (*self.raw).email) + } + } +} + +/// Try to borrow a `&str` from `ptr`, given that `ptr` may be null or +/// refer to ill-formed UTF-8. Give the result a lifetime as if it were +/// borrowed from `_owner`. +/// +/// Safety: if `ptr` is non-null, it must point to a null-terminated C +/// string that is safe to access. +unsafe fn char_ptr_to_str(_owner: &T, ptr: *const c_char) -> Option<&str> { + if ptr.is_null() { + return None; + } else { + CStr::from_ptr(ptr).to_str().ok() + } +} + diff --git a/libgit2-rs-safe/src/git/raw.rs b/libgit2-rs-safe/src/git/raw.rs new file mode 100644 index 0000000..846c460 --- /dev/null +++ b/libgit2-rs-safe/src/git/raw.rs @@ -0,0 +1,56 @@ +#![allow(dead_code, non_camel_case_types)] + +use std::os::raw::{c_int, c_char, c_uchar}; + +#[link(name = "git2")] +extern { + pub fn git_libgit2_init() -> c_int; + pub fn git_libgit2_shutdown() -> c_int; + pub fn giterr_last() -> *const git_error; + + pub fn git_repository_open(out: *mut *mut git_repository, path: *const c_char) -> c_int; + pub fn git_repository_free(repo: *mut git_repository); + + pub fn git_reference_name_to_id(out: *mut git_oid, + repo: *mut git_repository, + reference: *const c_char) -> c_int; + + pub fn git_commit_lookup(out: *mut *mut git_commit, + repo: *mut git_repository, + id: *const git_oid) -> c_int; + + pub fn git_commit_author(commit: *const git_commit) -> *const git_signature; + pub fn git_commit_message(commit: *const git_commit) -> *const c_char; + pub fn git_commit_free(commit: *mut git_commit); +} + +pub enum git_repository {} +pub enum git_commit {} + +#[repr(C)] +pub struct git_error { + pub message: *const c_char, + pub klass: c_int +} + +pub const GIT_OID_RAWSZ: usize = 20; + +#[repr(C)] +pub struct git_oid { + pub id: [c_uchar; GIT_OID_RAWSZ] +} + +pub type git_time_t = i64; + +#[repr(C)] +pub struct git_time { + pub time: git_time_t, + pub offset: c_int +} + +#[repr(C)] +pub struct git_signature { + pub name: *const c_char, + pub email: *const c_char, + pub when: git_time +} diff --git a/libgit2-rs-safe/src/main.rs b/libgit2-rs-safe/src/main.rs new file mode 100644 index 0000000..df5d1af --- /dev/null +++ b/libgit2-rs-safe/src/main.rs @@ -0,0 +1,24 @@ +mod git; + +extern crate libc; + +fn main() { + let path = std::env::args_os().skip(1).next() + .expect("usage: libgit2-rs PATH"); + + let repo = git::Repository::open(&path) + .expect("opening repository"); + + let commit_oid = repo.reference_name_to_id("HEAD") + .expect("looking up 'HEAD' reference"); + + let commit = repo.find_commit(&commit_oid) + .expect("looking up commit"); + + let author = commit.author(); + println!("{} <{}>\n", + author.name().unwrap_or("(none)"), + author.email().unwrap_or("none")); + + println!("{}", commit.message().unwrap_or("(none)")); +} diff --git a/libgit2-rs/.gitignore b/libgit2-rs/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/libgit2-rs/.gitignore @@ -0,0 +1 @@ +target diff --git a/libgit2-rs/Cargo.lock b/libgit2-rs/Cargo.lock new file mode 100644 index 0000000..e6d167d --- /dev/null +++ b/libgit2-rs/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "libgit2-rs" +version = "0.1.0" + diff --git a/libgit2-rs/Cargo.toml b/libgit2-rs/Cargo.toml new file mode 100644 index 0000000..cedd57f --- /dev/null +++ b/libgit2-rs/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "libgit2-rs" +version = "0.1.0" +authors = ["You "] +build = "build.rs" + +[dependencies] diff --git a/libgit2-rs/build.rs b/libgit2-rs/build.rs new file mode 100644 index 0000000..ee72958 --- /dev/null +++ b/libgit2-rs/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-search=native=/home/jimb/libgit2-0.25.1/build"); +} diff --git a/libgit2-rs/src/main.rs b/libgit2-rs/src/main.rs new file mode 100644 index 0000000..4a89e01 --- /dev/null +++ b/libgit2-rs/src/main.rs @@ -0,0 +1,67 @@ +mod raw; + +use std::ffi::CStr; +use std::os::raw::c_int; + +fn check(activity: &'static str, status: c_int) -> c_int { + if status < 0 { + unsafe { + let error = &*raw::giterr_last(); + println!("error while {}: {} ({})", + activity, + CStr::from_ptr(error.message).to_string_lossy(), + error.klass); + std::process::exit(1); + } + } + + status +} + +unsafe fn show_commit(commit: *const raw::git_commit) { + let author = raw::git_commit_author(commit); + + let name = CStr::from_ptr((*author).name).to_string_lossy(); + let email = CStr::from_ptr((*author).email).to_string_lossy(); + println!("{} <{}>\n", name, email); + + let message = raw::git_commit_message(commit); + println!("{}", CStr::from_ptr(message).to_string_lossy()); +} + +use std::ffi::CString; +use std::mem; +use std::ptr; +use std::os::raw::c_char; + +fn main() { + let path = std::env::args().skip(1).next() + .expect("usage: git-toy PATH"); + let path = CString::new(path) + .expect("path contains null characters"); + + unsafe { + check("initializing library", raw::git_libgit2_init()); + + let mut repo = ptr::null_mut(); + check("opening repository", + raw::git_repository_open(&mut repo, path.as_ptr())); + + let c_name = b"HEAD\0".as_ptr() as *const c_char; + let mut oid = mem::uninitialized(); + check("looking up HEAD", + raw::git_reference_name_to_id(&mut oid, repo, c_name)); + + let mut commit = ptr::null_mut(); + check("looking up commit", + raw::git_commit_lookup(&mut commit, repo, &oid)); + + show_commit(commit); + + raw::git_commit_free(commit); + + raw::git_repository_free(repo); + + check("shutting down library", raw::git_libgit2_shutdown()); + } +} diff --git a/libgit2-rs/src/raw.rs b/libgit2-rs/src/raw.rs new file mode 100644 index 0000000..d8a2d64 --- /dev/null +++ b/libgit2-rs/src/raw.rs @@ -0,0 +1,55 @@ +#![allow(non_camel_case_types)] + +use std::os::raw::{c_int, c_char, c_uchar}; + +#[link(name = "git2")] +extern { + pub fn git_libgit2_init() -> c_int; + pub fn git_libgit2_shutdown() -> c_int; + pub fn giterr_last() -> *const git_error; + + pub fn git_repository_open(out: *mut *mut git_repository, + path: *const c_char) -> c_int; + pub fn git_repository_free(repo: *mut git_repository); + + pub fn git_reference_name_to_id(out: *mut git_oid, + repo: *mut git_repository, + reference: *const c_char) -> c_int; + + pub fn git_commit_lookup(out: *mut *mut git_commit, + repo: *mut git_repository, + id: *const git_oid) -> c_int; + + pub fn git_commit_author(commit: *const git_commit) -> *const git_signature; + pub fn git_commit_message(commit: *const git_commit) -> *const c_char; + pub fn git_commit_free(commit: *mut git_commit); +} + +pub enum git_repository {} +pub enum git_commit {} + +#[repr(C)] +pub struct git_error { + pub message: *const c_char, + pub klass: c_int +} + +#[repr(C)] +pub struct git_oid { + pub id: [c_uchar; 20] +} + +pub type git_time_t = i64; + +#[repr(C)] +pub struct git_time { + pub time: git_time_t, + pub offset: c_int +} + +#[repr(C)] +pub struct git_signature { + pub name: *const c_char, + pub email: *const c_char, + pub when: git_time +} diff --git a/queue/.gitignore b/queue/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/queue/.gitignore @@ -0,0 +1 @@ +target diff --git a/queue/Cargo.lock b/queue/Cargo.lock new file mode 100644 index 0000000..7b7d4f2 --- /dev/null +++ b/queue/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "queue" +version = "0.1.0" + diff --git a/queue/Cargo.toml b/queue/Cargo.toml new file mode 100644 index 0000000..d7765e2 --- /dev/null +++ b/queue/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "queue" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/queue/src/lib.rs b/queue/src/lib.rs new file mode 100644 index 0000000..13a5f47 --- /dev/null +++ b/queue/src/lib.rs @@ -0,0 +1,109 @@ +/// A last-in, first-out queue of characters. +pub struct Queue { + older: Vec, // older elements, eldest last. + younger: Vec // younger elements, youngest last. +} + +impl Queue { + /// Push a character onto the back of a queue. + pub fn push(&mut self, c: char) { + self.younger.push(c); + } + + /// Pop a character off the front of a queue. Return `Some(c)` if there + /// was a character to pop, or `None` if the queue was empty. + pub fn pop(&mut self) -> Option { + if self.older.is_empty() { + if self.younger.is_empty() { + return None; + } + + // Bring the elements in younger over to older, and put them in + // the promised order. + use std::mem::swap; + swap(&mut self.older, &mut self.younger); + self.older.reverse(); + } + + // Now older is guaranteed to have something. Vec's pop method + // already returns an Option, so we're set. + self.older.pop() + } +} + +#[test] +fn test_push_pop() { + let mut q = Queue { older: Vec::new(), younger: Vec::new() }; + + q.push('0'); + q.push('1'); + assert_eq!(q.pop(), Some('0')); + + q.push('∞'); + assert_eq!(q.pop(), Some('1')); + assert_eq!(q.pop(), Some('∞')); + assert_eq!(q.pop(), None); + + (&mut q).push('0'); + (&mut q).push('1'); + assert_eq!(q.pop(), Some('0')); + assert_eq!(q.pop(), Some('1')); +} + +impl Queue { + pub fn is_empty(&self) -> bool { + self.older.is_empty() && self.younger.is_empty() + } +} + +#[test] +fn test_is_empty() { + let mut q = Queue { older: Vec::new(), younger: Vec::new() }; + + assert!(q.is_empty()); + q.push('☉'); + assert!(!q.is_empty()); + q.pop(); + assert!(q.is_empty()); +} + +impl Queue { + pub fn split(self) -> (Vec, Vec) { + (self.older, self.younger) + } +} + +#[test] +fn test_split() { + let mut q = Queue { older: Vec::new(), younger: Vec::new() }; + + q.push('P'); + q.push('D'); + assert_eq!(q.pop(), Some('P')); + q.push('X'); + + let (older, younger) = q.split(); + // q is now uninitialized. + assert_eq!(older, vec!['D']); + assert_eq!(younger, vec!['X']); +} + +impl Queue { + pub fn new() -> Queue { + Queue { older: Vec::new(), younger: Vec::new() } + } +} + +#[test] +fn test_new() { + let mut q = Queue::new(); + + q.push('*'); + q.push('1'); + assert_eq!(q.pop(), Some('*')); + + q.push('∞'); + assert_eq!(q.pop(), Some('1')); + assert_eq!(q.pop(), Some('∞')); + assert_eq!(q.pop(), None); +} diff --git a/ref-with-flag/.gitignore b/ref-with-flag/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/ref-with-flag/.gitignore @@ -0,0 +1 @@ +target diff --git a/ref-with-flag/Cargo.lock b/ref-with-flag/Cargo.lock new file mode 100644 index 0000000..9395dbd --- /dev/null +++ b/ref-with-flag/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "ref-with-flag" +version = "0.1.0" + diff --git a/ref-with-flag/Cargo.toml b/ref-with-flag/Cargo.toml new file mode 100644 index 0000000..c90ee11 --- /dev/null +++ b/ref-with-flag/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "ref-with-flag" +version = "0.1.0" +authors = ["You "] + +[dependencies] diff --git a/ref-with-flag/src/lib.rs b/ref-with-flag/src/lib.rs new file mode 100644 index 0000000..2c435fb --- /dev/null +++ b/ref-with-flag/src/lib.rs @@ -0,0 +1,50 @@ +#![allow(dead_code)] + +mod ref_with_flag { + use std::marker::PhantomData; + use std::mem::align_of; + + /// A `&T` and a `bool`, wrapped up in a single word. + /// The type `T` must require at least two-byte alignment. + /// + /// If you're the kind of programmer who's never met a pointer whose + /// 2⁰-bit you didn't want to steal, well, now you can do it safely! + /// ("But it's not nearly as exciting this way...") + pub struct RefWithFlag<'a, T: 'a> { + ptr_and_bit: usize, + behaves_like: PhantomData<&'a T> // occupies no space + } + + impl<'a, T: 'a> RefWithFlag<'a, T> { + pub fn new(ptr: &'a T, bit: bool) -> RefWithFlag { + assert!(align_of::() % 2 == 0); + RefWithFlag { + ptr_and_bit: ptr as *const T as usize | bit as usize, + behaves_like: PhantomData + } + } + + pub fn as_ref(&self) -> &'a T { + let ptr = (self.ptr_and_bit & !1) as *const T; + unsafe { + &*ptr + } + } + + pub fn as_bool(&self) -> bool { + self.ptr_and_bit & 1 != 0 + } + } +} + +mod ref_with_flag_tests { + #[test] + fn use_ref_with_flag() { + use ref_with_flag::RefWithFlag; + + let vec = vec![10, 20, 30]; + let pab = RefWithFlag::new(&vec, true); + assert_eq!(pab.as_ref()[1], 20); + assert_eq!(pab.as_bool(), true); + } +}