mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-24 19:16:31 +00:00
refactor: remove unicode dep and simplify UntaggedEither impl
This commit is contained in:
parent
62891bb2db
commit
5a215ebbfb
5 changed files with 105 additions and 48 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -2783,7 +2783,6 @@ dependencies = [
|
||||||
"toml 0.8.13",
|
"toml 0.8.13",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"unicode-ellipsis",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3290,16 +3289,6 @@ version = "2.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217"
|
checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ellipsis"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "924ab5b8c3fed9966b8cde2dc7169146331cba3dacba97cbd0e8866e7cfd4dff"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-segmentation",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
|
@ -3315,12 +3304,6 @@ dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-segmentation"
|
|
||||||
version = "1.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
|
|
|
@ -19,7 +19,7 @@ cadence = "1"
|
||||||
crates-index = { version = "2", default-features = false, features = ["git"] }
|
crates-index = { version = "2", default-features = false, features = ["git"] }
|
||||||
derive_more = "0.99"
|
derive_more = "0.99"
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
either = "1.12.0"
|
either = "1.12"
|
||||||
font-awesome-as-a-crate = "0.3"
|
font-awesome-as-a-crate = "0.3"
|
||||||
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
||||||
hyper = { version = "0.14.10", features = ["full"] }
|
hyper = { version = "0.14.10", features = ["full"] }
|
||||||
|
@ -34,15 +34,14 @@ relative-path = { version = "1", features = ["serde"] }
|
||||||
reqwest = { version = "0.12", features = ["json"] }
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
route-recognizer = "0.3"
|
route-recognizer = "0.3"
|
||||||
rustsec = "0.29"
|
rustsec = "0.29"
|
||||||
semver = { version = "1.0", features = ["serde"] }
|
semver = { version = "1", features = ["serde"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
serde_with = "3.8.1"
|
serde_with = "3"
|
||||||
tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros", "sync", "time"] }
|
tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros", "sync", "time"] }
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
tracing = "0.1.30"
|
tracing = "0.1.30"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
unicode-ellipsis = "0.2.0"
|
|
||||||
|
|
||||||
[target.'cfg(any())'.dependencies]
|
[target.'cfg(any())'.dependencies]
|
||||||
gix = { version = "0.63", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls"] }
|
gix = { version = "0.63", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls"] }
|
||||||
|
|
|
@ -10,7 +10,6 @@ use once_cell::sync::Lazy;
|
||||||
use route_recognizer::{Params, Router};
|
use route_recognizer::{Params, Router};
|
||||||
use semver::VersionReq;
|
use semver::VersionReq;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use unicode_ellipsis::truncate_str;
|
|
||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
mod views;
|
mod views;
|
||||||
|
@ -25,9 +24,11 @@ use crate::{
|
||||||
repo::RepoPath,
|
repo::RepoPath,
|
||||||
SubjectPath,
|
SubjectPath,
|
||||||
},
|
},
|
||||||
utils::common::{UntaggedEither, WrappedBool},
|
utils::common::{safe_truncate, UntaggedEither, WrappedBool},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MAX_SUBJECT_WIDTH: usize = 100;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
enum StatusFormat {
|
enum StatusFormat {
|
||||||
Html,
|
Html,
|
||||||
|
@ -430,10 +431,13 @@ static SELF_BASE_URL: Lazy<String> =
|
||||||
pub struct ExtraConfig {
|
pub struct ExtraConfig {
|
||||||
/// Badge style to show
|
/// Badge style to show
|
||||||
style: BadgeStyle,
|
style: BadgeStyle,
|
||||||
|
|
||||||
/// Whether the inscription _"dependencies"_ should be abbreviated as _"deps"_ in the badge.
|
/// Whether the inscription _"dependencies"_ should be abbreviated as _"deps"_ in the badge.
|
||||||
compact: bool,
|
compact: bool,
|
||||||
|
|
||||||
/// Custom text on the left (it's the same concept as `label` in shields.io).
|
/// Custom text on the left (it's the same concept as `label` in shields.io).
|
||||||
subject: Option<String>,
|
subject: Option<String>,
|
||||||
|
|
||||||
/// Path in which the crate resides within the repository
|
/// Path in which the crate resides within the repository
|
||||||
path: Option<String>,
|
path: Option<String>,
|
||||||
}
|
}
|
||||||
|
@ -447,7 +451,7 @@ impl ExtraConfig {
|
||||||
|
|
||||||
impl<T> QueryParam<T> {
|
impl<T> QueryParam<T> {
|
||||||
fn opt(self) -> Option<T> {
|
fn opt(self) -> Option<T> {
|
||||||
self.0.into_either().left()
|
either::Either::from(self.0).left()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,7 +463,6 @@ impl ExtraConfig {
|
||||||
path: Option<String>,
|
path: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_WIDTH: usize = 100;
|
|
||||||
let extra_config = qs
|
let extra_config = qs
|
||||||
.and_then(|qs| serde_urlencoded::from_str::<ExtraConfigPartial>(qs).ok())
|
.and_then(|qs| serde_urlencoded::from_str::<ExtraConfigPartial>(qs).ok())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
@ -477,8 +480,21 @@ impl ExtraConfig {
|
||||||
subject: extra_config
|
subject: extra_config
|
||||||
.subject
|
.subject
|
||||||
.filter(|t| !t.is_empty())
|
.filter(|t| !t.is_empty())
|
||||||
.map(|t| truncate_str(&t, MAX_WIDTH).into()),
|
.map(|subject| safe_truncate(&subject, MAX_SUBJECT_WIDTH).to_owned()),
|
||||||
path: extra_config.path,
|
path: extra_config.path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns subject for badge.
|
||||||
|
///
|
||||||
|
/// Returns `subject` if set, or "dependencies" / "deps" depending on value of `compact`.
|
||||||
|
pub(crate) fn subject(&self) -> &str {
|
||||||
|
if let Some(subject) = &self.subject {
|
||||||
|
subject
|
||||||
|
} else if self.compact {
|
||||||
|
"deps"
|
||||||
|
} else {
|
||||||
|
"dependencies"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,7 @@ pub fn badge(
|
||||||
analysis_outcome: Option<&AnalyzeDependenciesOutcome>,
|
analysis_outcome: Option<&AnalyzeDependenciesOutcome>,
|
||||||
badge_knobs: ExtraConfig,
|
badge_knobs: ExtraConfig,
|
||||||
) -> Badge {
|
) -> Badge {
|
||||||
let subject = if let Some(subject) = badge_knobs.subject {
|
let subject = badge_knobs.subject().to_owned();
|
||||||
subject
|
|
||||||
} else if badge_knobs.compact {
|
|
||||||
"deps".into()
|
|
||||||
} else {
|
|
||||||
"dependencies".into()
|
|
||||||
};
|
|
||||||
|
|
||||||
let opts = match analysis_outcome {
|
let opts = match analysis_outcome {
|
||||||
Some(outcome) => {
|
Some(outcome) => {
|
||||||
|
|
|
@ -41,31 +41,19 @@ impl<L, R> From<Either<L, R>> for UntaggedEither<L, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L, R> UntaggedEither<L, R> {
|
|
||||||
pub fn into_either(self) -> Either<L, R> {
|
|
||||||
self.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A generic newtype which serialized using `Display` and deserialized using `FromStr`.
|
/// A generic newtype which serialized using `Display` and deserialized using `FromStr`.
|
||||||
#[derive(Default, Clone, DeserializeFromStr, SerializeDisplay)]
|
#[derive(Default, Clone, DeserializeFromStr, SerializeDisplay)]
|
||||||
pub struct SerdeDisplayFromStr<T>(pub T);
|
pub struct SerdeDisplayFromStr<T>(pub T);
|
||||||
|
|
||||||
impl<T> From<T> for SerdeDisplayFromStr<T> {
|
|
||||||
fn from(value: T) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug> Debug for SerdeDisplayFromStr<T> {
|
impl<T: Debug> Debug for SerdeDisplayFromStr<T> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
self.0.fmt(f)
|
Debug::fmt(&self.0, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Display> Display for SerdeDisplayFromStr<T> {
|
impl<T: Display> Display for SerdeDisplayFromStr<T> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
self.0.fmt(f)
|
Display::fmt(&self.0, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +61,7 @@ impl<T: FromStr> FromStr for SerdeDisplayFromStr<T> {
|
||||||
type Err = T::Err;
|
type Err = T::Err;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(s.parse::<T>()?.into())
|
s.parse::<T>().map(Self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,3 +71,80 @@ impl<T: FromStr> FromStr for SerdeDisplayFromStr<T> {
|
||||||
/// are used. The Wrap type here forces the deserialization process to
|
/// are used. The Wrap type here forces the deserialization process to
|
||||||
/// be delegated to `FromStr`.
|
/// be delegated to `FromStr`.
|
||||||
pub type WrappedBool = SerdeDisplayFromStr<bool>;
|
pub type WrappedBool = SerdeDisplayFromStr<bool>;
|
||||||
|
|
||||||
|
/// Returns truncated string accounting for multi-byte characters.
|
||||||
|
pub(crate) fn safe_truncate(s: &str, len: usize) -> &str {
|
||||||
|
if len == 0 {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.len() <= len {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.is_char_boundary(len) {
|
||||||
|
return &s[0..len];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only 3 cases possible: 1, 2, or 3 bytes need to be removed for a new,
|
||||||
|
// valid UTF-8 string to appear when truncated, just enumerate them,
|
||||||
|
// Underflow is not possible since position 0 is always a valid boundary.
|
||||||
|
|
||||||
|
if let Some((slice, _rest)) = s.split_at_checked(len - 1) {
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((slice, _rest)) = s.split_at_checked(len - 2) {
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((slice, _rest)) = s.split_at_checked(len - 3) {
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable!("all branches covered");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn safe_truncation() {
|
||||||
|
assert_eq!(safe_truncate("", 0), "");
|
||||||
|
assert_eq!(safe_truncate("", 1), "");
|
||||||
|
assert_eq!(safe_truncate("", 9), "");
|
||||||
|
|
||||||
|
assert_eq!(safe_truncate("a", 0), "");
|
||||||
|
assert_eq!(safe_truncate("a", 1), "a");
|
||||||
|
assert_eq!(safe_truncate("a", 9), "a");
|
||||||
|
|
||||||
|
assert_eq!(safe_truncate("lorem\nipsum", 0), "");
|
||||||
|
assert_eq!(safe_truncate("lorem\nipsum", 5), "lorem");
|
||||||
|
assert_eq!(safe_truncate("lorem\nipsum", usize::MAX), "lorem\nipsum");
|
||||||
|
|
||||||
|
assert_eq!(safe_truncate("café", 1), "c");
|
||||||
|
assert_eq!(safe_truncate("café", 2), "ca");
|
||||||
|
assert_eq!(safe_truncate("café", 3), "caf");
|
||||||
|
assert_eq!(safe_truncate("café", 4), "caf");
|
||||||
|
assert_eq!(safe_truncate("café", 5), "café");
|
||||||
|
|
||||||
|
// 2-byte char
|
||||||
|
assert_eq!(safe_truncate("é", 0), "");
|
||||||
|
assert_eq!(safe_truncate("é", 1), "");
|
||||||
|
assert_eq!(safe_truncate("é", 2), "é");
|
||||||
|
|
||||||
|
// 3-byte char
|
||||||
|
assert_eq!(safe_truncate("⊕", 0), "");
|
||||||
|
assert_eq!(safe_truncate("⊕", 1), "");
|
||||||
|
assert_eq!(safe_truncate("⊕", 2), "");
|
||||||
|
assert_eq!(safe_truncate("⊕", 3), "⊕");
|
||||||
|
|
||||||
|
// 4-byte char
|
||||||
|
assert_eq!(safe_truncate("🦊", 0), "");
|
||||||
|
assert_eq!(safe_truncate("🦊", 1), "");
|
||||||
|
assert_eq!(safe_truncate("🦊", 2), "");
|
||||||
|
assert_eq!(safe_truncate("🦊", 3), "");
|
||||||
|
assert_eq!(safe_truncate("🦊", 4), "🦊");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue