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

Loading…
Cancel
Save