From bfa78c80a63e348ea7ce991ee35566d1852c0ca9 Mon Sep 17 00:00:00 2001 From: Ivan L Date: Mon, 15 Jul 2024 01:24:38 +0400 Subject: [PATCH] adjust clippy warnings --- types/src/angle/dd.rs | 19 +++++++------ types/src/angle/degree.rs | 34 +++++++++++++++-------- types/src/angle/dms_dd.rs | 15 ++++++---- types/src/angle/errors.rs | 8 +++--- types/src/angle/mod.rs | 5 +++- types/src/coord/lat.rs | 20 +++++++------- types/src/coord/lon.rs | 25 ++++++++++------- types/src/coord/mod.rs | 4 +-- types/src/coord/point.rs | 58 +++++++++++++++++++-------------------- types/src/lib.rs | 6 ---- types/src/utils.rs | 21 ++++++++++---- 11 files changed, 120 insertions(+), 95 deletions(-) diff --git a/types/src/angle/dd.rs b/types/src/angle/dd.rs index 70650c6..c2cdd91 100644 --- a/types/src/angle/dd.rs +++ b/types/src/angle/dd.rs @@ -178,6 +178,9 @@ impl DecimalDegree { Ok(()) } + // no panic is possible because the precision + // does not allow for too big whole-degree value (max=2^32 / 10^7 ~= 429) + #[allow(clippy::missing_panics_doc)] /// The whole number of degrees in the angle pub fn degrees(self) -> u16 { let degrees = self.units / Self::units_in_deg(); @@ -760,7 +763,7 @@ mod tests { #[test] fn print_zero_as_dms() { let d = DecimalDegree::default(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "0°"); } @@ -773,14 +776,14 @@ mod tests { #[test] fn print_right_as_dms() { let d: DecimalDegree = 90.try_into().unwrap(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "90°"); } #[test] fn print_fraction_as_dms() { let d = DecimalDegree::from_deg_and_fraction(60, 5_467_182).unwrap(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "60°32′48.186″"); } @@ -788,7 +791,7 @@ mod tests { fn print_fraction_as_dms_without_milli() { for f in 0..3 { let d = DecimalDegree::from_deg_and_fraction(60, 5_466_666 + f).unwrap(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "60°32′48″"); } } @@ -797,7 +800,7 @@ mod tests { fn print_fraction_as_dms_without_seconds() { for f in 0..3 { let d = DecimalDegree::from_deg_and_fraction(60, 5_333_332 + f).unwrap(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "60°32′"); } } @@ -805,7 +808,7 @@ mod tests { #[test] fn print_overflow_fraction_as_dms() { let d = DecimalDegree::from_deg_and_fraction(59, 9_999_999).unwrap(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "60°"); } @@ -858,7 +861,7 @@ mod tests { } #[test] - #[should_panic(expected = "assertion failed")] + #[should_panic(expected = "sum overflowed")] fn summing_dms_accumulate_errors() { let min = DecimalDegree::from_dms(0, 0, 0, 1).unwrap(); @@ -868,7 +871,7 @@ mod tests { acc = acc + min; } - assert_eq!(acc.milli_arc_seconds(), 10); + assert_eq!(acc.milli_arc_seconds(), 10, "sum overflowed"); } #[test] diff --git a/types/src/angle/degree.rs b/types/src/angle/degree.rs index 94d3829..8fa6f77 100644 --- a/types/src/angle/degree.rs +++ b/types/src/angle/degree.rs @@ -77,16 +77,30 @@ macro_rules! impl_conv_traits { /// Use with caution: the floating numbers has bad precision in the fraction part fn try_from(value: f64) -> Result { - if value.is_sign_negative() { - return Err(OutOfRange::Degrees); + fn f64_to_u64(value: f64) -> Result { + if value < 0.0 { + return Err("Value cannot be negative"); + } + + #[allow(clippy::cast_precision_loss)] + if value > u64::MAX as f64 { + return Err("Value is too large for u64"); + } + + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + Ok(value as u64) } // prevent wrapping around - let integer = value.floor() as u64; + let integer = f64_to_u64(value.floor()).map_err(|_| { + assert!(value.is_sign_negative()); + OutOfRange::Degrees + })?; let integer = integer.try_into().map_err(|_| OutOfRange::Degrees)?; let precision = Self::$fraction_multiplier_func(); - let fraction = (value.fract() * f64::from(precision)).round() as u64; + let fraction = (value.fract() * f64::from(precision)).round(); + let fraction = f64_to_u64(fraction).map_err(|_| OutOfRange::DegreeFraction)?; let fraction = fraction .try_into() .map_err(|_| OutOfRange::DegreeFraction)?; @@ -187,23 +201,19 @@ pub(super) fn parse_dms_re(is_ascii: bool, arc_seconds_fd: usize) -> String { r#"(?x) # enables verbose mode (to allow these comments) ^ # match the whole line from the start (?P[123]?\d{{1,2}}) # mandatory degree VALUE (0..=399) - requires more validation! - {} # degree sign (can be mandatory or optional) + {deg} # degree sign (can be mandatory or optional) (?:\x20? # minutes and seconds group optionally started with the space (?P[0-5]?\d) # minutes VALUE (0..=59) - {} # arcminute sign + {min} # arcminute sign (?:\x20? # seconds with the decimal fraction group optionally started with the space (?P[0-5]?\d) # whole seconds VALUE (0..=59) (?: # fractions of arcsecond with the decimal dot - \.(?P\d{{1,{precision}}}) # fractions of arcsecond VALUE (up to [precision] digits, 0..=99) + \.(?P\d{{1,{arc_seconds_fd}}}) # fractions of arcsecond VALUE (up to [precision] digits, 0..=99) )? # fractions of arcsecond are optional - {} # arcsecond sign + {sec} # arcsecond sign )? # seconds are optional )? # minutes and seconds are optional $ # match the whole line till the end "#, - deg, - min, - sec, - precision = arc_seconds_fd ) } diff --git a/types/src/angle/dms_dd.rs b/types/src/angle/dms_dd.rs index e0283a0..b5153af 100644 --- a/types/src/angle/dms_dd.rs +++ b/types/src/angle/dms_dd.rs @@ -210,6 +210,9 @@ impl AccurateDegree { Ok(()) } + // no panic is possible because the precision + // does not allow for too big whole-degree value (max=2^32 / 9*10^6 ~= 477) + #[allow(clippy::missing_panics_doc)] /// The whole number of degrees in the angle pub fn degrees(self) -> u16 { let degrees = self.units / Self::units_in_deg(); @@ -789,7 +792,7 @@ mod tests { #[test] fn print_zero_as_dms() { let d = AccurateDegree::default(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "0°"); } @@ -802,14 +805,14 @@ mod tests { #[test] fn print_right_as_dms() { let d: AccurateDegree = 90.try_into().unwrap(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "90°"); } #[test] fn print_fraction_as_dms() { let d = AccurateDegree::from_deg_and_fraction(60, 546_718).unwrap(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "60°32′48.18″"); } @@ -817,7 +820,7 @@ mod tests { fn print_fraction_as_dms_without_milli() { for f in 0..3 { let d = AccurateDegree::from_deg_and_fraction(60, 546_666 + f).unwrap(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "60°32′48″"); } } @@ -826,7 +829,7 @@ mod tests { fn print_fraction_as_dms_without_seconds() { for f in 0..3 { let d = AccurateDegree::from_deg_and_fraction(60, 533_332 + f).unwrap(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "60°32′"); } } @@ -834,7 +837,7 @@ mod tests { #[test] fn print_overflow_fraction_as_dms() { let d = AccurateDegree::from_deg_and_fraction(59, 999_999).unwrap(); - let s = format!("{:#}", d); + let s = format!("{d:#}"); assert_eq!(s, "60°"); } diff --git a/types/src/angle/errors.rs b/types/src/angle/errors.rs index bb38c60..8202f2a 100644 --- a/types/src/angle/errors.rs +++ b/types/src/angle/errors.rs @@ -37,7 +37,7 @@ impl fmt::Display for OutOfRange { Self::ArcMilliSeconds => "Angle's arc millisecond should be less than 1000", }; - write!(f, "{}", msg) + write!(f, "{msg}") } } @@ -60,9 +60,9 @@ impl fmt::Display for ParseAngleError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Cannot parse angle: ")?; match self { - Self::Range(inner) => write!(f, "{}", inner), - Self::Float(inner) => write!(f, "{}", inner), - Self::Int(inner) => write!(f, "{}", inner), + Self::Range(inner) => write!(f, "{inner}"), + Self::Float(inner) => write!(f, "{inner}"), + Self::Int(inner) => write!(f, "{inner}"), Self::DmsNotation => write!(f, "not a Degree-Minute-Second notation"), } } diff --git a/types/src/angle/mod.rs b/types/src/angle/mod.rs index 8803597..1e684e9 100644 --- a/types/src/angle/mod.rs +++ b/types/src/angle/mod.rs @@ -88,6 +88,7 @@ pub trait Angle: Self::straight().checked_sub(&self) } + #[must_use] /// Adjacent angle which sum to a [complete](trait.AngleNames.html#tymethod.complete) angle fn explement(self) -> Self { Self::complete() @@ -95,6 +96,7 @@ pub trait Angle: .expect("Current implementation stores angles <=360 degrees") } + #[must_use] /// Difference between the angles by modulo independent of the order fn abs_diff(self, rhs: Self) -> Self { let diff = self.checked_sub(&rhs).or_else(|| rhs.checked_sub(&self)); @@ -146,7 +148,8 @@ pub trait Angle: } } -pub(super) trait UnitsAngle: Angle { +#[allow(clippy::module_name_repetitions)] +pub trait UnitsAngle: Angle { type Units: CheckedAdd + CheckedSub; fn from_units(u: Self::Units) -> Result; diff --git a/types/src/coord/lat.rs b/types/src/coord/lat.rs index 763a592..1a4ee10 100644 --- a/types/src/coord/lat.rs +++ b/types/src/coord/lat.rs @@ -137,6 +137,7 @@ impl Neg for Latitude { fn neg(self) -> Self::Output { let angle = self.angle_from_equator(); + #[allow(clippy::option_if_let_else)] let opposite_pole = match self.hemisphere() { Some(pole) => -pole, // just a convention for equator, it means nothing when constructing a Latitude @@ -157,9 +158,9 @@ impl TryFrom for Latitude { } } -impl TryFrom<(i8, u8, u8, u16)> for Latitude +impl TryFrom<(i8, u8, u8, u16)> for Latitude where - A: TryFrom<(u16, u8, u8, u16), Error = ::NumErr>, + A: Angle + TryFrom<(u16, u8, u8, u16), Error = ::NumErr>, { type Error = A::NumErr; @@ -257,25 +258,24 @@ where } } -impl fmt::Display for Latitude +impl fmt::Display for Latitude where - A: fmt::Display, + A: Angle + fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let angle = self.angle_from_equator(); if f.alternate() { - write!(f, "{:#}", angle)?; + write!(f, "{angle:#}")?; if let Some(hemisphere) = self.hemisphere() { - write!(f, "{:#}", hemisphere) - } else { - Ok(()) + write!(f, "{hemisphere:#}")?; } + Ok(()) } else { - if let Some(South) = self.hemisphere() { + if self.hemisphere() == Some(South) { write!(f, "-")?; } - write!(f, "{}", angle) + write!(f, "{angle}") } } } diff --git a/types/src/coord/lon.rs b/types/src/coord/lon.rs index 231e120..9282de1 100644 --- a/types/src/coord/lon.rs +++ b/types/src/coord/lon.rs @@ -94,6 +94,12 @@ impl Longitude { } } + // no panic is possible because + // the angle always stays: + // - (angle - PI) for angle >= PI (>= 0) + // - (angle + PI) for angle < PI (< 2*PI) + #[allow(clippy::missing_panics_doc)] + #[must_use] /// Diametrically opposite meridian /// which together with the current one defines /// the hemisphere (great circle) @@ -203,9 +209,9 @@ impl TryFrom for Longitude { } } -impl TryFrom<(i16, u8, u8, u16)> for Longitude +impl TryFrom<(i16, u8, u8, u16)> for Longitude where - A: TryFrom<(u16, u8, u8, u16), Error = ::NumErr>, + A: Angle + TryFrom<(u16, u8, u8, u16), Error = ::NumErr>, { type Error = A::NumErr; @@ -295,26 +301,25 @@ where } } -impl fmt::Display for Longitude +impl fmt::Display for Longitude where - A: fmt::Display, + A: Angle + fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let angle = self.angle(); if f.alternate() { - write!(f, "{:#}", angle)?; + write!(f, "{angle:#}")?; if let Some(direction) = self.direction() { - write!(f, "{:#}", direction) - } else { - Ok(()) + write!(f, "{direction:#}")?; } + Ok(()) } else { - if let Some(West) = self.direction() { + if self.direction() == Some(West) { write!(f, "-")?; } - write!(f, "{}", angle) + write!(f, "{angle}") } } } diff --git a/types/src/coord/mod.rs b/types/src/coord/mod.rs index 90bc19d..2073729 100644 --- a/types/src/coord/mod.rs +++ b/types/src/coord/mod.rs @@ -25,7 +25,7 @@ impl fmt::Display for ParseCoordinateError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Coordinate parsing failed: ")?; match self { - Self::Angle(inner) => write!(f, "{}", inner), + Self::Angle(inner) => write!(f, "{inner}"), Self::EmptyString => write!(f, "empty string provided"), Self::NoHemisphere => write!(f, "direction (hemisphere) was not detected"), } @@ -128,7 +128,7 @@ macro_rules! bool_enum { ($name:ident: $truthy:ident and $falsy:ident; parse from $true_ch:literal:$false_ch:literal with $parse_err:ident) => { use self::$name::{$falsy, $truthy}; - #[derive(Debug, Copy, Clone, PartialEq)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum $name { $truthy, $falsy, diff --git a/types/src/coord/point.rs b/types/src/coord/point.rs index 09cc977..2611f9b 100644 --- a/types/src/coord/point.rs +++ b/types/src/coord/point.rs @@ -26,7 +26,7 @@ pub struct Point { impl Point { /// Construct a point from the given latitude and longitude - pub fn new(lat: Latitude, lon: Longitude) -> Self { + pub const fn new(lat: Latitude, lon: Longitude) -> Self { Self { lat, lon } } @@ -63,6 +63,7 @@ impl Point { self.lat.is_pole() } + #[must_use] /// The diametrically opposite point pub fn antipodal(&self) -> Self { Self { @@ -89,9 +90,9 @@ impl PartialEq for Point { } } -impl fmt::Display for Point +impl fmt::Display for Point where - A: fmt::Display, + A: Angle + fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { @@ -121,8 +122,8 @@ mod tests_accur { assert!(sp.lon.angle().is_zero()); assert!(sp.lon.direction().is_none()); - assert_eq!(format!("{}", sp), "(-90°,0°)"); - assert_eq!(format!("{:#}", sp), "Lat: 90°S, Long: 0°"); + assert_eq!(format!("{sp}"), "(-90°,0°)"); + assert_eq!(format!("{sp:#}"), "Lat: 90°S, Long: 0°"); } #[test] @@ -134,8 +135,8 @@ mod tests_accur { assert!(origin.lon.angle().is_zero()); assert!(origin.lon.direction().is_none()); - assert_eq!(format!("{}", origin), "(0°,0°)"); - assert_eq!(format!("{:#}", origin), "Lat: 0°, Long: 0°"); + assert_eq!(format!("{origin}"), "(0°,0°)"); + assert_eq!(format!("{origin:#}"), "Lat: 0°, Long: 0°"); } #[test] @@ -163,9 +164,9 @@ mod tests_accur { ); assert_eq!(saint_petersburg.lon.direction(), Some(East)); - assert_eq!(format!("{}", saint_petersburg), "(59.937500°,30.308611°)"); + assert_eq!(format!("{saint_petersburg}"), "(59.937500°,30.308611°)"); assert_eq!( - format!("{:#}", saint_petersburg), + format!("{saint_petersburg:#}"), "Lat: 59°56′15″N, Long: 30°18′31″E" ); } @@ -195,8 +196,8 @@ mod tests_accur { ); assert_eq!(santiago.lon.direction(), Some(West)); - assert_eq!(format!("{}", santiago), "(-33.450000°,-70.666667°)"); - assert_eq!(format!("{:#}", santiago), "Lat: 33°27′S, Long: 70°40′W"); + assert_eq!(format!("{santiago}"), "(-33.450000°,-70.666667°)"); + assert_eq!(format!("{santiago:#}"), "Lat: 33°27′S, Long: 70°40′W"); } #[test] @@ -215,11 +216,8 @@ mod tests_accur { ); assert_eq!(point.lon.direction(), Some(East)); - assert_eq!(format!("{}", point), "(-33.462222°,70.671044°)"); - assert_eq!( - format!("{:#}", point), - "Lat: 33°27′44″S, Long: 70°40′15.76″E" - ); + assert_eq!(format!("{point}"), "(-33.462222°,70.671044°)"); + assert_eq!(format!("{point:#}"), "Lat: 33°27′44″S, Long: 70°40′15.76″E"); } #[test] @@ -238,9 +236,9 @@ mod tests_accur { ); assert_eq!(point.lon.direction(), Some(West)); - assert_eq!(format!("{}", point), "(33.462314°,-167.183900°)"); + assert_eq!(format!("{point}"), "(33.462314°,-167.183900°)"); assert_eq!( - format!("{:#}", point), + format!("{point:#}"), "Lat: 33°27′44.33″N, Long: 167°11′2.04″W" ); } @@ -286,8 +284,8 @@ mod tests_dec { assert!(sp.lon.angle().is_zero()); assert!(sp.lon.direction().is_none()); - assert_eq!(format!("{}", sp), "(-90°,0°)"); - assert_eq!(format!("{:#}", sp), "Lat: 90°S, Long: 0°"); + assert_eq!(format!("{sp}"), "(-90°,0°)"); + assert_eq!(format!("{sp:#}"), "Lat: 90°S, Long: 0°"); } #[test] @@ -299,8 +297,8 @@ mod tests_dec { assert!(origin.lon.angle().is_zero()); assert!(origin.lon.direction().is_none()); - assert_eq!(format!("{}", origin), "(0°,0°)"); - assert_eq!(format!("{:#}", origin), "Lat: 0°, Long: 0°"); + assert_eq!(format!("{origin}"), "(0°,0°)"); + assert_eq!(format!("{origin:#}"), "Lat: 0°, Long: 0°"); } #[test] @@ -328,9 +326,9 @@ mod tests_dec { ); assert_eq!(saint_petersburg.lon.direction(), Some(East)); - assert_eq!(format!("{}", saint_petersburg), "(59.9375000°,30.3086111°)"); + assert_eq!(format!("{saint_petersburg}"), "(59.9375000°,30.3086111°)"); assert_eq!( - format!("{:#}", saint_petersburg), + format!("{saint_petersburg:#}"), "Lat: 59°56′15″N, Long: 30°18′31″E" ); } @@ -360,8 +358,8 @@ mod tests_dec { ); assert_eq!(santiago.lon.direction(), Some(West)); - assert_eq!(format!("{}", santiago), "(-33.4500000°,-70.6666667°)"); - assert_eq!(format!("{:#}", santiago), "Lat: 33°27′S, Long: 70°40′W"); + assert_eq!(format!("{santiago}"), "(-33.4500000°,-70.6666667°)"); + assert_eq!(format!("{santiago:#}"), "Lat: 33°27′S, Long: 70°40′W"); } #[test] @@ -380,9 +378,9 @@ mod tests_dec { ); assert_eq!(point.lon.direction(), Some(East)); - assert_eq!(format!("{}", point), "(-33.4622222°,70.6710439°)"); + assert_eq!(format!("{point}"), "(-33.4622222°,70.6710439°)"); assert_eq!( - format!("{:#}", point), + format!("{point:#}"), "Lat: 33°27′44″S, Long: 70°40′15.758″E" ); } @@ -403,9 +401,9 @@ mod tests_dec { ); assert_eq!(point.lon.direction(), Some(West)); - assert_eq!(format!("{}", point), "(33.4623147°,-167.1839014°)"); + assert_eq!(format!("{point}"), "(33.4623147°,-167.1839014°)"); assert_eq!( - format!("{:#}", point), + format!("{point:#}"), "Lat: 33°27′44.333″N, Long: 167°11′2.045″W" ); } diff --git a/types/src/lib.rs b/types/src/lib.rs index ff1a11a..71cb2ba 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -13,19 +13,13 @@ #![warn(deprecated_in_future)] #![warn(elided_lifetimes_in_paths)] #![warn(explicit_outlives_requirements)] -#![warn(indirect_structural_match)] -#![warn(invalid_html_tags)] #![warn(keyword_idents)] #![warn(macro_use_extern_crate)] #![warn(meta_variable_misuse)] #![warn(missing_copy_implementations)] -#![warn(missing_crate_level_docs)] #![warn(missing_debug_implementations)] -#![warn(missing_doc_code_examples)] #![warn(missing_docs)] #![warn(non_ascii_idents)] -#![warn(pointer_structural_match)] -#![warn(private_doc_tests)] #![warn(single_use_lifetimes)] #![warn(trivial_casts)] #![warn(trivial_numeric_casts)] diff --git a/types/src/utils.rs b/types/src/utils.rs index a6c7918..44649fa 100644 --- a/types/src/utils.rs +++ b/types/src/utils.rs @@ -19,18 +19,23 @@ macro_rules! enum_trivial_from_impl { }; } +mod private { + #[derive(Debug, Copy, Clone)] + pub struct Token; +} + /// Allow conversion of a signed value into its unsigned equivalent /// by dropping the sign away pub trait ToUnsigned: Default + Copy + PartialOrd + Neg { /// represent the source (signed) type as target (unsigned) type - fn as_type(self) -> U; + fn as_type(self, token: private::Token) -> U; /// Converts to unsigned absolute value, also preserving the 'is negative' flag fn abs_and_sign(self) -> (U, bool) { if self >= Self::default() { - (self.as_type(), true) + (self.as_type(private::Token), true) } else { - ((-self).as_type(), false) + ((-self).as_type(private::Token), false) } } } @@ -38,15 +43,19 @@ pub trait ToUnsigned: Default + Copy + PartialOrd + Neg { macro_rules! impl_abs_and_sign { ($from: tt -> $to: ty) => { impl ToUnsigned<$to> for $from { - fn as_type(self) -> $to { + // can not be called from another module + // with the protection of `Token`, + // therefore, the sign is always checked explicitly + #[allow(clippy::cast_sign_loss)] + fn as_type(self, _token: private::Token) -> $to { self as $to } } }; ($same: ty) => { - impl ToUnsigned<$same> for $same { - fn as_type(self) -> Self { + impl ToUnsigned for $same { + fn as_type(self, _token: private::Token) -> Self { self } }