From: Lukas Lueg Date: Mon, 4 Dec 2017 15:43:53 +0000 (+0100) Subject: Break crate-descriptions at char-, not byte-boundary, avoiding a panic X-Git-Tag: archive/raspbian/0.35.0-2+rpi1~3^2^2^2^2^2^2^2~22^2~4^2~32^2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=be48e92f17ebc5986e121f11bac784fc863303e9;p=cargo.git Break crate-descriptions at char-, not byte-boundary, avoiding a panic Long running descriptions were truncated at byte-boundary, leading to a panic during registry::search(); we now break at char-boundary. Also take length of names-column into account to shorten descriptions. Fixes #4771. --- diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index 18f2d5cea..6bcbd4af2 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -1,4 +1,4 @@ -use std::env; +use std::{cmp, env}; use std::fs::{self, File}; use std::iter::repeat; use std::time::Duration; @@ -455,12 +455,16 @@ pub fn search(query: &str, index: Option, limit: u8, reg: Option) -> CargoResult<()> { - fn truncate_with_ellipsis(s: &str, max_length: usize) -> String { - if s.len() < max_length { - s.to_string() - } else { - format!("{}…", &s[..max_length - 1]) + fn truncate_with_ellipsis(s: &str, max_width: usize) -> String { + // We should truncate at grapheme-boundary and compute character-widths, + // yet the dependencies on unicode-segmentation and unicode-width are + // not worth it. + let mut chars = s.chars(); + let mut prefix = (&mut chars).take(max_width - 1).collect::(); + if chars.next().is_some() { + prefix.push('…'); } + prefix } let (mut registry, _) = registry(config, None, index, reg)?; @@ -468,19 +472,23 @@ pub fn search(query: &str, CargoError::from(format!("failed to retrieve search results from the registry: {}", e)) })?; - let list_items = crates.iter() - .map(|krate| ( - format!("{} = \"{}\"", krate.name, krate.max_version), - krate.description.as_ref().map(|desc| - truncate_with_ellipsis(&desc.replace("\n", " "), 128)) - )) - .collect::>(); - let description_margin = list_items.iter() - .map(|&(ref left, _)| left.len() + 4) + let names = crates.iter() + .map(|krate| format!("{} = \"{}\"", krate.name, krate.max_version)) + .collect::>(); + + let description_margin = names.iter() + .map(|s| s.len() + 4) .max() - .unwrap_or(0); + .unwrap_or_default(); + + let description_length = cmp::max(80, 128 - description_margin); + + let descriptions = crates.iter() + .map(|krate| + krate.description.as_ref().map(|desc| + truncate_with_ellipsis(&desc.replace("\n", " "), description_length))); - for (name, description) in list_items.into_iter() { + for (name, description) in names.into_iter().zip(descriptions) { let line = match description { Some(desc) => { let space = repeat(' ').take(description_margin - name.len())