diff --git a/src/gopher.rs b/src/gopher.rs index 6b29407..cccefeb 100644 --- a/src/gopher.rs +++ b/src/gopher.rs @@ -1,3 +1,4 @@ +use gopher; use std::io; use std::io::{Read, Write}; use std::net::TcpStream; @@ -38,6 +39,29 @@ impl Type { } } +pub fn type_for_char(c: char) -> Option { + match c { + '0' => Some(Type::Text), + '1' => Some(Type::Menu), + '2' => Some(Type::CSOEntity), + '3' => Some(Type::Error), + '4' => Some(Type::Binhex), + '5' => Some(Type::DOSFile), + '6' => Some(Type::UUEncoded), + '7' => Some(Type::Search), + '8' => Some(Type::Telnet), + '9' => Some(Type::Binary), + '+' => Some(Type::Mirror), + 'g' => Some(Type::GIF), + 'T' => Some(Type::Telnet3270), + 'h' => Some(Type::HTML), + 'i' => Some(Type::Info), + 's' => Some(Type::Sound), + 'd' => Some(Type::Document), + _ => None, + } +} + // Fetches a URL and returns a raw Gopher response. pub fn fetch_url(url: &str) -> io::Result { let (_, host, port, sel) = parse_url(url); @@ -63,6 +87,21 @@ pub fn fetch(host: &str, port: &str, selector: &str) -> io::Result { } } +// Downloads a file to a local path. +pub fn download(path: &str, host: &str, port: &str, selector: &str) -> io::Result<()> { + let mut file = std::fs::File::create(path)?; + + TcpStream::connect(format!("{}:{}", host, port)) + .and_then(|mut stream| { + stream.write(format!("{}\r\n", selector).as_ref()); + Ok(stream) + }) + .and_then(|mut stream| { + std::io::copy(&mut stream, &mut file); + Ok(()) + }) +} + enum Parsing { Host, Port, @@ -109,21 +148,10 @@ pub fn parse_url<'a>(url: &'a str) -> (Type, &'a str, &'a str, &'a str) { }; let mut chars = sel.chars(); - if let (Some('/'), Some(t), Some('/')) = (chars.nth(0), chars.nth(0), chars.nth(0)) { - typ = match t { - '0' => { - sel = &sel[2..]; - Type::Text - } - '1' => { - sel = &sel[2..]; - Type::Menu - } - 'h' => { - sel = &sel[2..]; - Type::HTML - } - _ => typ, + if let (Some('/'), Some(c), Some('/')) = (chars.nth(0), chars.nth(0), chars.nth(0)) { + if let Some(t) = gopher::type_for_char(c) { + typ = t; + sel = &sel[2..]; } } diff --git a/src/menu.rs b/src/menu.rs index d89a078..09d40b9 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -1,3 +1,4 @@ +use gopher; use gopher::Type; use std::io::stdout; use std::io::Write; @@ -282,27 +283,9 @@ impl Menu { let mut longest = 0; for line in raw.split_terminator("\n") { if let Some(c) = line.chars().nth(0) { - let typ = match c { - '0' => Type::Text, - '1' => Type::Menu, - '2' => Type::CSOEntity, - '3' => Type::Error, - '4' => Type::Binhex, - '5' => Type::DOSFile, - '6' => Type::UUEncoded, - '7' => Type::Search, - '8' => Type::Telnet, - '9' => Type::Binary, - '+' => Type::Mirror, - 'g' => Type::GIF, - 'T' => Type::Telnet3270, - 'h' => Type::HTML, - 'i' => Type::Info, - 's' => Type::Sound, - 'd' => Type::Document, - '.' => continue, - '\n' => continue, - _ => continue, + let typ = match gopher::type_for_char(c) { + Some(t) => t, + None => continue, }; // build string URL @@ -322,10 +305,8 @@ impl Menu { // auto-prepend gopher type to selector if let Some(first_char) = parts[0].chars().nth(0) { - if first_char == '0' || first_char == '1' || first_char == 'h' { - url.push_str("/"); - url.push(first_char); - } + url.push_str("/"); + url.push(first_char); } if parts.len() > 1 {