|
|
|
|
@ -23,9 +23,9 @@ use status::Status;
|
|
|
|
|
mod link; |
|
|
|
|
use link::Link; |
|
|
|
|
|
|
|
|
|
mod absolute; |
|
|
|
|
mod content; |
|
|
|
|
mod history; |
|
|
|
|
mod absolute; |
|
|
|
|
|
|
|
|
|
const HELP: &str = "Welcome to Asuka Gemini browser! |
|
|
|
|
|
|
|
|
|
@ -151,15 +151,15 @@ fn visit_url(s: &mut Cursive, url: &Url) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
match absolute::make(url.as_str()) { |
|
|
|
|
Ok(url) => { |
|
|
|
|
match content::get_data(&url) { |
|
|
|
|
Ok((meta, new_content)) => { |
|
|
|
|
history::append(url.as_str()); |
|
|
|
|
draw_content(s, &url, meta, new_content); |
|
|
|
|
} |
|
|
|
|
Err(msg) => { |
|
|
|
|
s.add_layer(Dialog::info(msg)); |
|
|
|
|
} |
|
|
|
|
Ok(url) => match content::get_data(&url) { |
|
|
|
|
Ok((meta, new_content)) => { |
|
|
|
|
history::append(url.as_str()); |
|
|
|
|
// handle meta header
|
|
|
|
|
let response = handle_response_status(s, &url, meta, new_content); |
|
|
|
|
draw_content(s, &url, response); |
|
|
|
|
} |
|
|
|
|
Err(msg) => { |
|
|
|
|
s.add_layer(Dialog::info(msg)); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
Err(_) => { |
|
|
|
|
@ -168,87 +168,12 @@ fn visit_url(s: &mut Cursive, url: &Url) {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn draw_content(s: &mut Cursive, url: &Url, meta: Vec<u8>, content: Vec<u8>) { |
|
|
|
|
let content_str = String::from_utf8_lossy(&content).to_string(); |
|
|
|
|
|
|
|
|
|
// handle meta header
|
|
|
|
|
handle_response_status(s, url, meta, content); |
|
|
|
|
|
|
|
|
|
let mut main_view = match s.find_id::<SelectView>("main") { |
|
|
|
|
Some(view) => view, |
|
|
|
|
None => panic!("Can't find main view."), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// set title and clear old content
|
|
|
|
|
set_title(s, url.as_str()); |
|
|
|
|
main_view.clear(); |
|
|
|
|
|
|
|
|
|
// draw new content lines
|
|
|
|
|
for line in content_str.lines() { |
|
|
|
|
match Link::from_str(line) { |
|
|
|
|
Ok(link) => match link { |
|
|
|
|
Link::Http(url, label) => { |
|
|
|
|
let mut formatted = StyledString::new(); |
|
|
|
|
let www_label = format!("{} [WWW]", label); |
|
|
|
|
formatted.append(StyledString::styled( |
|
|
|
|
www_label, |
|
|
|
|
Style::from(Color::Dark(BaseColor::Green)).combine(Effect::Bold), |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
let data = object! { |
|
|
|
|
"type" => "www", |
|
|
|
|
"url" => url.to_string() |
|
|
|
|
}; |
|
|
|
|
main_view.add_item(formatted, json::stringify(data)) |
|
|
|
|
} |
|
|
|
|
Link::Gopher(url, label) => { |
|
|
|
|
let mut formatted = StyledString::new(); |
|
|
|
|
let gopher_label = format!("{} [Gopher]", label); |
|
|
|
|
formatted.append(StyledString::styled( |
|
|
|
|
gopher_label, |
|
|
|
|
Style::from(Color::Light(BaseColor::Magenta)).combine(Effect::Bold), |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
let data = object! { |
|
|
|
|
"type" => "gopher", |
|
|
|
|
"url" => url.to_string() |
|
|
|
|
}; |
|
|
|
|
main_view.add_item(formatted, json::stringify(data)) |
|
|
|
|
} |
|
|
|
|
Link::Gemini(url, label) => { |
|
|
|
|
let mut formatted = StyledString::new(); |
|
|
|
|
formatted.append(StyledString::styled( |
|
|
|
|
label, |
|
|
|
|
Style::from(Color::Light(BaseColor::Blue)).combine(Effect::Bold), |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
let data = object! { |
|
|
|
|
"type" => "gemini", |
|
|
|
|
"url" => url.to_string() |
|
|
|
|
}; |
|
|
|
|
main_view.add_item(formatted, json::stringify(data)) |
|
|
|
|
} |
|
|
|
|
Link::Relative(url, label) => { |
|
|
|
|
let mut formatted = StyledString::new(); |
|
|
|
|
formatted.append(StyledString::styled( |
|
|
|
|
label, |
|
|
|
|
Style::from(Color::Light(BaseColor::Blue)).combine(Effect::Bold), |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
let data = object! { |
|
|
|
|
"type" => "gemini", |
|
|
|
|
"url" => url.to_string() |
|
|
|
|
}; |
|
|
|
|
main_view.add_item(formatted, json::stringify(data)) |
|
|
|
|
} |
|
|
|
|
Link::Unknown(_, _) => (), |
|
|
|
|
}, |
|
|
|
|
Err(_) => main_view.add_item(str::replace(line, "\t", " "), String::from("0")), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn handle_response_status(s: &mut Cursive, url: &Url, meta: Vec<u8>, content: Vec<u8>) { |
|
|
|
|
fn handle_response_status( |
|
|
|
|
s: &mut Cursive, |
|
|
|
|
url: &Url, |
|
|
|
|
meta: Vec<u8>, |
|
|
|
|
content: Vec<u8>, |
|
|
|
|
) -> Option<Vec<u8>> { |
|
|
|
|
let url_copy = url.clone(); |
|
|
|
|
let meta_str = String::from_utf8_lossy(&meta).to_string(); |
|
|
|
|
|
|
|
|
|
@ -257,38 +182,126 @@ fn handle_response_status(s: &mut Cursive, url: &Url, meta: Vec<u8>, content: Ve
|
|
|
|
|
Status::Success(meta) => { |
|
|
|
|
if meta.starts_with("text/") { |
|
|
|
|
// display text files.
|
|
|
|
|
{} |
|
|
|
|
Some(content) |
|
|
|
|
} else { |
|
|
|
|
// download and try to open the rest.
|
|
|
|
|
content::download(content); |
|
|
|
|
return; |
|
|
|
|
None |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Status::Gone(_meta) => { |
|
|
|
|
s.add_layer(Dialog::info("Sorry page is gone.")); |
|
|
|
|
return; |
|
|
|
|
None |
|
|
|
|
} |
|
|
|
|
Status::RedirectTemporary(new_url) | Status::RedirectPermanent(new_url) => { |
|
|
|
|
return follow_link(s, &new_url) |
|
|
|
|
follow_link(s, &new_url); |
|
|
|
|
None |
|
|
|
|
} |
|
|
|
|
Status::TransientCertificateRequired(_meta) |
|
|
|
|
| Status::AuthorisedCertificatedRequired(_meta) => { |
|
|
|
|
s.add_layer(Dialog::info( |
|
|
|
|
"You need a valid certificate to access this page.", |
|
|
|
|
)); |
|
|
|
|
return; |
|
|
|
|
None |
|
|
|
|
} |
|
|
|
|
Status::Input(message) => { |
|
|
|
|
prompt_for_answer(s, url_copy, message); |
|
|
|
|
None |
|
|
|
|
} |
|
|
|
|
other_status => { |
|
|
|
|
s.add_layer(Dialog::info(format!("ERROR: {:?}", other_status))); |
|
|
|
|
return; |
|
|
|
|
None |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
None |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn draw_content(s: &mut Cursive, url: &Url, content: Option<Vec<u8>>) { |
|
|
|
|
match content { |
|
|
|
|
Some(data) => { |
|
|
|
|
let mut main_view = match s.find_id::<SelectView>("main") { |
|
|
|
|
Some(view) => view, |
|
|
|
|
None => panic!("Can't find main view."), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// set title and clear old content
|
|
|
|
|
set_title(s, url.as_str()); |
|
|
|
|
main_view.clear(); |
|
|
|
|
|
|
|
|
|
let content_str = String::from_utf8_lossy(&data).to_string(); |
|
|
|
|
|
|
|
|
|
// draw new content lines
|
|
|
|
|
for line in content_str.lines() { |
|
|
|
|
match Link::from_str(line) { |
|
|
|
|
Ok(link) => match link { |
|
|
|
|
Link::Http(url, label) => { |
|
|
|
|
let mut formatted = StyledString::new(); |
|
|
|
|
let www_label = format!("{} [WWW]", label); |
|
|
|
|
formatted.append(StyledString::styled( |
|
|
|
|
www_label, |
|
|
|
|
Style::from(Color::Dark(BaseColor::Green)).combine(Effect::Bold), |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
let data = object! { |
|
|
|
|
"type" => "www", |
|
|
|
|
"url" => url.to_string() |
|
|
|
|
}; |
|
|
|
|
main_view.add_item(formatted, json::stringify(data)) |
|
|
|
|
} |
|
|
|
|
Link::Gopher(url, label) => { |
|
|
|
|
let mut formatted = StyledString::new(); |
|
|
|
|
let gopher_label = format!("{} [Gopher]", label); |
|
|
|
|
formatted.append(StyledString::styled( |
|
|
|
|
gopher_label, |
|
|
|
|
Style::from(Color::Light(BaseColor::Magenta)).combine(Effect::Bold), |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
let data = object! { |
|
|
|
|
"type" => "gopher", |
|
|
|
|
"url" => url.to_string() |
|
|
|
|
}; |
|
|
|
|
main_view.add_item(formatted, json::stringify(data)) |
|
|
|
|
} |
|
|
|
|
Link::Gemini(url, label) => { |
|
|
|
|
let mut formatted = StyledString::new(); |
|
|
|
|
formatted.append(StyledString::styled( |
|
|
|
|
label, |
|
|
|
|
Style::from(Color::Light(BaseColor::Blue)).combine(Effect::Bold), |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
let data = object! { |
|
|
|
|
"type" => "gemini", |
|
|
|
|
"url" => url.to_string() |
|
|
|
|
}; |
|
|
|
|
main_view.add_item(formatted, json::stringify(data)) |
|
|
|
|
} |
|
|
|
|
Link::Relative(url, label) => { |
|
|
|
|
let mut formatted = StyledString::new(); |
|
|
|
|
formatted.append(StyledString::styled( |
|
|
|
|
label, |
|
|
|
|
Style::from(Color::Light(BaseColor::Blue)).combine(Effect::Bold), |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
let data = object! { |
|
|
|
|
"type" => "gemini", |
|
|
|
|
"url" => url.to_string() |
|
|
|
|
}; |
|
|
|
|
main_view.add_item(formatted, json::stringify(data)) |
|
|
|
|
} |
|
|
|
|
Link::Unknown(_, _) => (), |
|
|
|
|
}, |
|
|
|
|
Err(_) => { |
|
|
|
|
main_view.add_item(str::replace(line, "\t", " "), String::from("0")) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
None => (), |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn set_title(s: &mut Cursive, text: &str) { |
|
|
|
|
let mut container = match s.find_id::<Dialog>("container") { |
|
|
|
|
Some(view) => view, |
|
|
|
|
|