Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ AGNOSTIC_TEST_IDS = `seq 1 3`

docopt: $(objects)

docopt.so: docopt.rs docopt.rc
$(RUSTC) -L ./rust-pcre docopt.rc
docopt.so: docopt.rs
$(RUSTC) docopt.rs

test_docopt: docopt.rs docopt.rc
$(RUSTC) docopt.rc --test -o test_docopt -L ./ -L ./rust-pcre
test_docopt: docopt.rs
$(RUSTC) docopt.rs --test -o test_docopt

run_tests:
./test_docopt

agnostic_testee: agnostic_testee.rs
$(RUSTC) agnostic_testee.rs -L ./ -L ./rust-pcre
$(RUSTC) agnostic_testee.rs -L ./

run_agnostic_tests:
python ./python_docopt/language_agnostic_test/language_agnostic_tester.py ./agnostic_testee $(AGNOSTIC_TEST_IDS)
Expand Down
5 changes: 2 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@ This is a port of Python docopt library http://github.com/docopt/docopt
Requirements
-------------

- Rust >= 0.4 // using bleeding-edge master branch of Rust
- Rust >= 0.8 // using bleeding-edge master branch of Rust
- GNU Make == 3.81 // you can try other versions as well


Building & tests
----------------
Run following commands to init and update git submodules::

git submodule init
git submodule update
git submodule update --init

To build and run language agnostic tests::

Expand Down
31 changes: 16 additions & 15 deletions agnostic_testee.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
extern mod std;
extern mod docopt;

use io::{ReaderUtil, WriterUtil};
use send_map::linear::LinearMap;
use std::json::LinearMap;

use std::rt::io;
use std::str;

/// Reads whole stream and outputs it as string
fn read_whole_stream_to_str(input_stream: io::Reader) -> ~str {
fn read_whole_stream_to_str(input_stream: &mut io::Reader) -> ~str {
let mut buf = ~[];
loop {
let ch = input_stream.read_byte();
if input_stream.eof() { break; }
buf.push(ch as u8);
do std::rt::io::io_error::cond.trap(|_| ()).inside {
loop {
match input_stream.read_byte() {
Some(c) => buf.push(c as char),
None => break,
}
}
}
str::from_bytes(buf)
str::from_chars(buf)
}


fn main () {

let input_stream = io::stdin();
let mut input_stream = io::stdin();

let doc: ~str = read_whole_stream_to_str(input_stream);
let doc: ~str = read_whole_stream_to_str(&mut input_stream as &mut io::Reader);

let docopt_result = docopt::docopt(copy doc);
let docopt_result = docopt::docopt(doc.clone());

match docopt_result {
Ok(options) => io::println(options.to_json().to_str()),
Ok(options) => io::println(format!("{}", options.to_str())),
Err(error_message) => io::println(error_message)

}

/* Testing various things */
io::println(docopt::printable_usage(copy doc));
io::println(docopt::printable_usage(doc.clone()));
}
6 changes: 0 additions & 6 deletions docopt.rc

This file was deleted.

125 changes: 66 additions & 59 deletions docopt.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,57 @@
use cmp::Eq;
use str::str;
#[link(name = "docopt",
vers = "0.1",
author = "Kirill Panshin")];
#[crate_type = "lib"];

use std::map::Map;
use send_map::linear::LinearMap;
use std::json;
extern mod std;
extern mod extra;
use std::cmp::Eq;
use std::hashmap::HashMap;
use std::str;
use extra::json;

use std::json::ToJson;
use extra::json::ToJson;


//Toplevel public function for parsing. Args are taken from os::args()
pub fn docopt(doc: ~str) -> Result<LinearMap<~str, json::Json>, ~str> {
pub fn docopt(doc: ~str) -> Result<HashMap<~str, json::Json>, ~str> {

let argv = os::args();
docopt_ext(copy doc, copy argv)
let argv = std::os::args();
docopt_ext(doc.clone(), argv.clone())
}

/// Toplevel public function for parsing doc. Arguments passed explicitly
pub fn docopt_ext(doc: ~str, argv: ~[~str]) -> Result<LinearMap<~str, json::Json>, ~str> {
pub fn docopt_ext(doc: ~str, argv: ~[~str]) -> Result<HashMap<~str, json::Json>, ~str> {

let mut options = LinearMap();
let mut options = HashMap::new();

/* TODO: insert data to map here */
options.insert(~"Arguments", argv.to_json());

if doc == ~"trigger_error" {
Err(str::append(~"Error: ", doc))
Err(format!("Error: {}", doc))
}
else {
Ok(move options)
Ok(options)
}
}


pub struct Option {
mut short: ~str,
mut long: ~str,
mut argcount: int,
mut value: ~str
short: ~str,
long: ~str,
argcount: int,
value: ~str
}


/// Parse token and return option object
pub fn Option(short: ~str, long: ~str, argcount: int, value: ~str) -> Option {
Option {
short: move short,
long: move long,
argcount: move argcount,
value: move value
short: short,
long: long,
argcount: argcount,
value: value
}
}

Expand All @@ -64,40 +69,41 @@ pub fn get_option() -> Option {
impl Option {

/// Parse token and return option object
fn parse(option_description: &str) {
let splitted = str::split_str_nonempty(
option_description.trim(), ~" ");
let mut (options, description) = match splitted.len() {
1 => (copy splitted[0], ~""),
2 => (copy splitted[0], copy splitted[1]),
_ => {io::println("Error: double space must appear only once");
fail}// Handle this situation more gracefully
fn parse(&mut self, option_description: &str) {
let splitted = option_description.trim().split_str_iter(" ").to_owned_vec();
let (options, description) = match splitted.len() {
1 => (splitted[0].to_owned(), ~""),
2 => (splitted[0].to_owned(), splitted[1].to_owned()),
_ => {fail!("Error: double space must appear only once");
}// Handle this situation more gracefully
};

let options = str::replace(options, ~",", ~" ");
let options = str::replace(options, ~"=", ~" ");
let splitted = str::split_char_nonempty(options, ' ');
let options = str::replace(options, ",", " ");
let options = str::replace(options, "=", " ");
let splitted = options.split_iter(' ').to_owned_vec();

for splitted.each() |part| {
if str::starts_with(*part, ~"--") {
self.long = copy *part;
for part in splitted.iter() {
if *part == "" {
// pass
} else if part.starts_with("--") {
self.long = part.to_owned();
}
else if str::starts_with(*part, ~"-") {
self.short = copy *part;
else if part.starts_with("-") {
self.short = part.to_owned();
}
else {
self.argcount = 1;
}
}

if self.argcount > 0 {
let splitted_desc = description.split_str(~"[default: ");
let splitted_desc = description.split_str_iter("[default: ").to_owned_vec();
self.value = match splitted_desc.len() {
1 => {~""},
2 => {splitted_desc[1].split_char(']')[0]},
_ => {io::println("Error: [default: VALUE] must \
appear only once");
fail} // May be handle this more gracefully
2 => {splitted_desc[1].split_iter(']').nth(0).unwrap().to_owned()},
_ => {fail!("Error: [default: VALUE] must \
appear only once");
} // May be handle this more gracefully
};
}
// TODO: parse default value '\[default: (.*)\]'
Expand All @@ -107,42 +113,43 @@ impl Option {
}


impl Option: Eq {
impl Eq for Option {
#[inline(always)]
pure fn eq(other: &Option) -> bool {
fn eq(&self, other: &Option) -> bool {
((self.short == other.short) && (self.long == other.long) && (self.argcount == other.argcount) && (self.value == other.value))
}
#[inline(always)]
pure fn ne(other: &Option) -> bool { !self.eq(other) }
fn ne(&self, other: &Option) -> bool { !self.eq(other) }
}


/// Print usage
pub fn printable_usage(doc: ~str) -> ~str {

let splitted = str::split_str_nonempty(doc, ~"Usage:");
let splitted = doc.split_str_iter("Usage:").to_owned_vec();
let (word_usage, usage) =match splitted.len() {
1 => (copy splitted[0], ~""),
2 => (copy splitted[0], copy splitted[1]),
_ => {io::println("Error in description: ``Usage:`` \
must appear only once");
fail // Handle more gracefully
1 => (splitted[0].to_owned(), ~""),
2 => (splitted[0].to_owned(), splitted[1].to_owned()),
_ => {fail!("Error in description: ``Usage:`` \
must appear only once");
// Handle more gracefully
}
};

fmt!("%s%s", word_usage, usage)
format!("{}{}", word_usage, usage)
}


#[cfg(test)]
mod tests {

fn check_option(token: ~str, option_args: (~str, ~str, int, ~str)) {
let option = get_option();
let mut option = super::get_option();
option.parse(token);
let (short, long, argcount, value) = copy option_args;
assert option == Option(copy short, copy long,
copy argcount, copy value);
let (short, long, argcount, value) = option_args;
assert!(option == super::Option(short.clone(), long.clone(),
argcount.clone(), value.clone()),
"Parsing: {}", token);
}

#[test]
Expand Down Expand Up @@ -176,13 +183,13 @@ mod tests {

#[test]
fn test_docopt_ext_ok() {
let result = docopt_ext(~"Usage: my_program", ~[]);
assert result.is_ok();
let result = super::docopt_ext(~"Usage: my_program", ~[]);
assert!(result.is_ok());
}

// #[test]
// fn test_docopt_ext_err() {
// let result = docopt::docopt_ext(~"Usage: my_program", ~[~"-h"]);
// let result = super::docopt::docopt_ext(~"Usage: my_program", ~[~"-h"]);
// assert result.is_err();
// }

Expand Down