Last active
August 29, 2015 14:12
-
-
Save aisamanra/798ff8b09fd933173127 to your computer and use it in GitHub Desktop.
Simple MadLibs-ey program as Rust practice
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::io::File; | |
use std::io::stdio::{stdin,print}; | |
use std::iter::range; | |
use std::os; | |
use std::rand::{Rng,task_rng}; | |
use std::vec::as_vec; | |
/* This program will take a file that describes a 'mad libs template' as | |
* argument and ask the user for the correct parts of speech in a random | |
* order, and then stitch the resulting mad lib together. This was a very | |
* quick-and-dirty version written while waiting for a plane, so don't | |
* expect it to be an exemplar of good Rust style. | |
*/ | |
/* stdin().read_line() will return strings with newlines, so this simply | |
* unwraps and truncates the string without the newline. */ | |
fn read_line() -> String { | |
let mut s = stdin().read_line().unwrap(); | |
let l = s.len(); | |
s.truncate(l - 1); | |
return s; | |
} | |
/* A MadLib is a sequence of pieces (which the user will not change) and | |
* a sequence of parts (which start out as parts of speech, and get | |
* replaced by user input before printing.) */ | |
struct MadLib { | |
pieces: Vec<String>, | |
parts: Vec<String>, | |
} | |
impl MadLib { | |
/* Given a string that represents a 'mad lib', construct the | |
* MadLib value from it. The string will have parts of speech | |
* in curly braces like "The {adjective} doggie", and having | |
* non-balanced curly braces or nested curly braces will cause | |
* the program to panic with a descriptive error message. */ | |
fn from_vec(str: &Vec<u8>) -> MadLib { | |
let mut pieces = vec![]; | |
let mut parts = vec![]; | |
let mut last_pos = 0; | |
let mut in_brace = false; | |
let add_to_list = |lst: &mut Vec<String>, pos: uint| { | |
let chunk = as_vec(str.slice_or_fail(&last_pos, &pos)); | |
lst.push(String::from_utf8(chunk.clone()).unwrap()); | |
last_pos = pos + 1; | |
}; | |
for (pos, &c) in str.iter().enumerate() { | |
match c { | |
0x7b => { | |
if in_brace { panic!("Error: unexpected '{'") }; | |
add_to_list(&mut pieces, pos); | |
in_brace = true; | |
} | |
0x7d => { | |
if !in_brace { panic!("Error: unexpected '}'") }; | |
add_to_list(&mut parts, pos); | |
in_brace = false; | |
} | |
_ => () | |
} | |
} | |
if in_brace { panic!("Error: unterminated '{'") }; | |
add_to_list(&mut pieces, str.len()); | |
assert!(pieces.len() == parts.len() + 1); | |
return MadLib { pieces: pieces, parts: parts }; | |
} | |
/* Replace the parts of speech with user-supplied input in random | |
* order. | |
*/ | |
fn fill_in(&mut self) -> &MadLib { | |
let mut idxs: Vec<uint> = range(0, self.parts.len()).collect(); | |
task_rng().shuffle(idxs.as_mut_slice()); | |
for &i in idxs.iter() { | |
print!("Enter a {}: ", self.parts[i]); | |
self.parts[i] = read_line(); | |
} | |
return self; | |
} | |
/* Print the mad lib as it should be seen on stdout. */ | |
fn print(&self) -> &MadLib { | |
for n in range(0, self.parts.len()) { | |
print(self.pieces[n].as_slice()); | |
print(self.parts[n].as_slice()); | |
} | |
print(self.pieces[self.pieces.len()-1].as_slice()); | |
return self; | |
} | |
} | |
/* Find the name of a madlib template (or panic if it is not given) and | |
* then construct the appropriate representation, fill it in, and print | |
* the resulting sentence. */ | |
fn main() { | |
let args = os::args(); | |
let contents = | |
if args.len() > 1 { | |
let name = args[1].as_slice(); | |
File::open(&Path::new(name)).read_to_end() | |
} else { | |
panic!("No filename given!"); | |
}; | |
MadLib::from_vec(&contents.unwrap()).fill_in().print(); | |
} | |
#[cfg(test)] | |
mod test { | |
use super::MadLib; | |
use std::thread::Thread; | |
#[test] | |
fn parse_correctly() { | |
let ref source = "a{b}cc{ddd}eeee".bytes().collect(); | |
let madlib = MadLib::from_vec(source); | |
assert_eq!(madlib.pieces.len(), 3); | |
assert_eq!(madlib.parts.len(), 2); | |
assert_eq!(madlib.pieces[0], "a"); | |
assert_eq!(madlib.pieces[1], "cc"); | |
assert_eq!(madlib.pieces[2], "eeee"); | |
assert_eq!(madlib.parts[0], "b"); | |
assert_eq!(madlib.parts[1], "ddd"); | |
} | |
#[test] | |
fn extra_open_curly_brace() { | |
let guard = Thread::spawn(move || { | |
let ref source = "a{b{c}".bytes().collect(); | |
let madlib = MadLib::from_vec(source); | |
}); | |
match guard.join() { | |
Ok(_) => { assert!(false) } | |
Err(_) => { assert!(true) } | |
} | |
} | |
#[test] | |
fn extra_close_curly_brace() { | |
let guard = Thread::spawn(move || { | |
let ref source = "a}b{c}".bytes().collect(); | |
let madlib = MadLib::from_vec(source); | |
}); | |
match guard.join() { | |
Ok(_) => { assert!(false) } | |
Err(_) => { assert!(true) } | |
} | |
} | |
#[test] | |
fn unterminated_curly_brace() { | |
let guard = Thread::spawn(move || { | |
let ref source = "a{b}c{d".bytes().collect(); | |
let madlib = MadLib::from_vec(source); | |
}); | |
match guard.join() { | |
Ok(_) => { assert!(false) } | |
Err(_) => { assert!(true) } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment