cache encoded response

pull/21/head
chris west 4 years ago
parent ba7d527165
commit e6583a81f3

@ -1,4 +1,4 @@
use std::io::Result; use std::{borrow::Cow, io::Result};
/// Encoding of Gopher response. Only UTF8 and CP437 are supported. /// Encoding of Gopher response. Only UTF8 and CP437 are supported.
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
@ -27,4 +27,17 @@ impl Encoding {
_ => Err(error!("Expected CP437 or UTF8 encoding")), _ => Err(error!("Expected CP437 or UTF8 encoding")),
} }
} }
/// Convert `response` into a String according to `encoding`.
pub fn encode<'res>(&self, response: &'res [u8]) -> Cow<'res, str> {
if matches!(self, Encoding::CP437) {
let mut converted = String::with_capacity(response.len());
for b in response {
converted.push_str(cp437::convert_byte(&b));
}
Cow::from(converted)
} else {
String::from_utf8_lossy(response)
}
}
} }

@ -8,7 +8,7 @@ use crate::{
terminal, terminal,
ui::{self, Action, Key, View, MAX_COLS, SCROLL_LINES}, ui::{self, Action, Key, View, MAX_COLS, SCROLL_LINES},
}; };
use std::{borrow::Cow, fmt, str}; use std::{fmt, str};
/// The Text View holds the raw Gopher response as well as information /// The Text View holds the raw Gopher response as well as information
/// about which lines should currently be displayed on screen. /// about which lines should currently be displayed on screen.
@ -19,6 +19,8 @@ pub struct Text {
url: String, url: String,
/// Gopher response /// Gopher response
raw_response: Vec<u8>, raw_response: Vec<u8>,
/// Encoded response
encoded_response: String,
/// Current scroll offset, in rows /// Current scroll offset, in rows
scroll: usize, scroll: usize,
/// Number of lines /// Number of lines
@ -150,8 +152,8 @@ impl View for Text {
} else { } else {
self.lines self.lines
}; };
let response = self.encoded_response();
let iter = wrap_text(&response, wrap) let iter = wrap_text(&self.encoded_response, wrap)
.into_iter() .into_iter()
.skip(self.scroll) .skip(self.scroll)
.take(limit); .take(limit);
@ -183,40 +185,28 @@ impl View for Text {
impl Text { impl Text {
/// Create a Text View from a raw Gopher response and a few options. /// Create a Text View from a raw Gopher response and a few options.
pub fn from(url: &str, response: Vec<u8>, config: Config, tls: bool) -> Text { pub fn from(url: &str, response: Vec<u8>, config: Config, tls: bool) -> Text {
let mut lines = 0;
let mut longest = 0;
let mut line_len = 0;
for &b in &response {
line_len += 1;
if b == b'\n' {
if line_len > longest {
longest = line_len;
}
line_len = 0;
lines += 1;
}
}
let mode = config.read().unwrap().mode; let mode = config.read().unwrap().mode;
let tor = config.read().unwrap().tor; let tor = config.read().unwrap().tor;
let encoding = config.read().unwrap().encoding; let encoding = config.read().unwrap().encoding;
let wide = config.read().unwrap().wide; let wide = config.read().unwrap().wide;
Text { let mut new = Text {
config, config,
url: url.into(), url: url.into(),
encoded_response: String::new(),
raw_response: response, raw_response: response,
scroll: 0, scroll: 0,
lines, lines: 0,
longest, longest: 0,
size: (0, 0), size: (0, 0),
mode, mode,
tls, tls,
tor, tor,
encoding, encoding,
wide, wide,
} };
new.encode_response();
new
} }
/// Toggle between our two encodings. /// Toggle between our two encodings.
@ -227,20 +217,20 @@ impl Text {
self.encoding = Encoding::UTF8; self.encoding = Encoding::UTF8;
} }
self.config.write().unwrap().encoding = self.encoding; self.config.write().unwrap().encoding = self.encoding;
self.encode_response();
Action::Redraw Action::Redraw
} }
/// Interpret `self.raw_response` according to `self.encoding`. /// Convert the response to a Rust String and cache metadata like
fn encoded_response(&self) -> Cow<str> { /// the number of lines.
if matches!(self.encoding, Encoding::CP437) { fn encode_response(&mut self) {
let mut converted = String::with_capacity(self.raw_response.len()); self.encoded_response = self.encoding.encode(&self.raw_response).into();
for b in &self.raw_response { let wrapped = wrap_text(
converted.push_str(cp437::convert_byte(&b)); self.encoded_response.as_ref(),
} self.config.read().unwrap().wrap,
Cow::from(converted) );
} else { self.lines = wrapped.len();
String::from_utf8_lossy(&self.raw_response) self.longest = wrapped.iter().map(|line| line.len()).max().unwrap_or(0) as usize;
}
} }
/// Final `self.scroll` value. /// Final `self.scroll` value.
@ -256,7 +246,7 @@ impl Text {
/// Splits a chunk of text into a vector of strings with at most /// Splits a chunk of text into a vector of strings with at most
/// `wrap` characters each. Tries to be smart and wrap at punctuation, /// `wrap` characters each. Tries to be smart and wrap at punctuation,
/// otherwise just wraps at `wrap`. /// otherwise just wraps at `wrap`.
fn wrap_text(lines: &str, wrap: usize) -> Vec<&str> { fn wrap_text(lines: &str, wrap: usize) -> Vec<&str> {
if wrap == 0 { if wrap == 0 {
return lines.split('\n').collect(); return lines.split('\n').collect();
@ -280,8 +270,13 @@ fn wrap_text(lines: &str, wrap: usize) -> Vec<&str> {
.find(|(_, c)| matches!(c, ' ' | '-' | ',' | '.' | ':')) .find(|(_, c)| matches!(c, ' ' | '-' | ',' | '.' | ':'))
{ {
out.push(&line[..=end]); out.push(&line[..=end]);
line = &line[end + 1..]; if end + 1 < line.len() {
len -= end; line = &line[end + 1..];
len -= end;
} else {
len = 0;
break;
}
continue; continue;
} }
} }

Loading…
Cancel
Save