Skip to content

Instantly share code, notes, and snippets.

@aisamanra
Last active August 29, 2015 14:12
Show Gist options
  • Save aisamanra/798ff8b09fd933173127 to your computer and use it in GitHub Desktop.
Save aisamanra/798ff8b09fd933173127 to your computer and use it in GitHub Desktop.
Simple MadLibs-ey program as Rust practice
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