From 64419c8b635c60018b792810b1854f1fdfa5824e Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Tue, 17 May 2022 13:43:01 +0100 Subject: [PATCH 1/3] Apply suggestions from Clippy --- src/lib.rs | 67 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d5c82f7..9b82090 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -372,6 +372,24 @@ pub struct Id<'a> { name: Cow<'a, str>, } +#[derive(Debug)] +pub enum IdError { + EmptyName, + InvalidStartChar(char), + InvalidChar(char) +} + +impl std::fmt::Display for IdError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IdError::EmptyName => write!(f, "Id cannot be empty"), + IdError::InvalidStartChar(c) => write!(f, "Id cannot begin with '{c}'"), + IdError::InvalidChar(c) => write!(f, "Id cannot contain '{c}'") + } + } +} +impl std::error::Error for IdError {} + impl<'a> Id<'a> { /// Creates an `Id` named `name`. /// @@ -387,19 +405,20 @@ impl<'a> Id<'a> { /// /// Passing an invalid string (containing spaces, brackets, /// quotes, ...) will return an empty `Err` value. - pub fn new>>(name: Name) -> Result, ()> { + pub fn new>>(name: Name) -> Result, IdError> { let name = name.into(); { let mut chars = name.chars(); match chars.next() { Some(c) if is_letter_or_underscore(c) => {} - _ => return Err(()), + Some(c) => return Err(IdError::InvalidStartChar(c)), + _ => return Err(IdError::EmptyName) } - if !chars.all(is_constituent) { - return Err(()) + if let Some(bad) = chars.find(|c| !is_constituent(*c)) { + return Err(IdError::InvalidChar(bad)) } } - return Ok(Id{ name: name }); + return Ok(Id{ name }); fn is_letter_or_underscore(c: char) -> bool { in_range('a', c, 'z') || in_range('A', c, 'Z') || c == '_' @@ -511,10 +530,10 @@ pub trait Labeller<'a,N,E> { /// Graphviz HTML label. pub fn escape_html(s: &str) -> String { s - .replace("&", "&") - .replace("\"", """) - .replace("<", "<") - .replace(">", ">") + .replace('&', "&") + .replace('"', """) + .replace('<', "<") + .replace('>', ">") } impl<'a> LabelText<'a> { @@ -558,9 +577,9 @@ impl<'a> LabelText<'a> { /// This includes quotes or suitable delimeters. pub fn to_dot_string(&self) -> String { match self { - &LabelStr(ref s) => format!("\"{}\"", LabelText::escape_default(s)), - &EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s[..])), - &HtmlStr(ref s) => format!("<{}>", s), + LabelStr(ref s) => format!("\"{}\"", LabelText::escape_default(s)), + EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s[..])), + HtmlStr(ref s) => format!("<{}>", s), } } @@ -650,25 +669,9 @@ impl Arrow { } -impl Into for [ArrowShape; 2] { - fn into(self) -> Arrow { - Arrow { - arrows: vec![self[0], self[1]], - } - } -} -impl Into for [ArrowShape; 3] { - fn into(self) -> Arrow { - Arrow { - arrows: vec![self[0], self[1], self[2]], - } - } -} -impl Into for [ArrowShape; 4] { - fn into(self) -> Arrow { - Arrow { - arrows: vec![self[0], self[1], self[2], self[3]], - } +impl From<[ArrowShape; N]> for Arrow { + fn from(shape: [ArrowShape; N]) -> Arrow { + Arrow {arrows: shape.to_vec() } } } @@ -932,7 +935,7 @@ pub fn render_opts<'a, for &s in arg { w.write_all(s.as_bytes())?; } - write!(w, "\n") + writeln!(w) } fn indent(w: &mut W) -> io::Result<()> { From cc4bf9bca99ffe73a3594e6c0092d8f39175f3b5 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Tue, 17 May 2022 13:54:37 +0100 Subject: [PATCH 2/3] Specifically implement ArrowShape conversion for array sizes --- src/lib.rs | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9b82090..8238a68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -373,22 +373,22 @@ pub struct Id<'a> { } #[derive(Debug)] -pub enum IdError { +pub enum IdError<'a> { EmptyName, - InvalidStartChar(char), - InvalidChar(char) + InvalidStartChar(char, Cow<'a, str>), + InvalidChar(char, Cow<'a, str>), } -impl std::fmt::Display for IdError { +impl<'a> std::fmt::Display for IdError<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { IdError::EmptyName => write!(f, "Id cannot be empty"), - IdError::InvalidStartChar(c) => write!(f, "Id cannot begin with '{c}'"), - IdError::InvalidChar(c) => write!(f, "Id cannot contain '{c}'") + IdError::InvalidStartChar(c, id) => write!(f, "Id cannot begin with `{c}` (`{id}`)"), + IdError::InvalidChar(c, id) => write!(f, "Id cannot contain `{c}` (`{id}`)"), } } } -impl std::error::Error for IdError {} +impl<'a> std::error::Error for IdError<'a> {} impl<'a> Id<'a> { /// Creates an `Id` named `name`. @@ -405,20 +405,20 @@ impl<'a> Id<'a> { /// /// Passing an invalid string (containing spaces, brackets, /// quotes, ...) will return an empty `Err` value. - pub fn new>>(name: Name) -> Result, IdError> { + pub fn new>>(name: Name) -> Result, IdError<'a>> { let name = name.into(); { let mut chars = name.chars(); match chars.next() { Some(c) if is_letter_or_underscore(c) => {} - Some(c) => return Err(IdError::InvalidStartChar(c)), - _ => return Err(IdError::EmptyName) + Some(c) => return Err(IdError::InvalidStartChar(c, name)), + None => return Err(IdError::EmptyName), } if let Some(bad) = chars.find(|c| !is_constituent(*c)) { - return Err(IdError::InvalidChar(bad)) + return Err(IdError::InvalidChar(bad, name)); } } - return Ok(Id{ name }); + return Ok(Id { name }); fn is_letter_or_underscore(c: char) -> bool { in_range('a', c, 'z') || in_range('A', c, 'Z') || c == '_' @@ -577,9 +577,9 @@ impl<'a> LabelText<'a> { /// This includes quotes or suitable delimeters. pub fn to_dot_string(&self) -> String { match self { - LabelStr(ref s) => format!("\"{}\"", LabelText::escape_default(s)), - EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s[..])), - HtmlStr(ref s) => format!("<{}>", s), + LabelStr(s) => format!("\"{}\"", LabelText::escape_default(s)), + EscStr(s) => format!("\"{}\"", LabelText::escape_str(&s[..])), + HtmlStr(s) => format!("<{}>", s), } } @@ -663,17 +663,23 @@ impl Arrow { let mut cow = String::new(); for arrow in &self.arrows { cow.push_str(&arrow.to_dot_string()); - }; + } cow } } - -impl From<[ArrowShape; N]> for Arrow { - fn from(shape: [ArrowShape; N]) -> Arrow { - Arrow {arrows: shape.to_vec() } +macro_rules! arrowshape_to_arrow { + ( $($n:expr), * ) => { + $( + impl From<[ArrowShape; $n]> for Arrow { + fn from(shape: [ArrowShape; $n]) -> Arrow { + Arrow {arrows: shape.to_vec() } + } + } + )* } } +arrowshape_to_arrow!(2, 3, 4); /// Arrow modifier that determines if the shape is empty or filled. #[derive(Clone, Copy, Hash, PartialEq, Eq)] From 002595292b7e914e65e5a669e0d21301cce58b11 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Fri, 30 Aug 2024 16:08:48 +0100 Subject: [PATCH 3/3] Apply new Clippy fixes --- src/lib.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8238a68..8fe828c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -432,7 +432,7 @@ impl<'a> Id<'a> { } pub fn as_slice(&'a self) -> &'a str { - &*self.name + &self.name } pub fn name(self) -> Cow<'a, str> { @@ -591,7 +591,7 @@ impl<'a> LabelText<'a> { match self { EscStr(s) => s, LabelStr(s) => if s.contains('\\') { - LabelText::escape_default(&*s).into() + LabelText::escape_default(&s).into() } else { s }, @@ -624,18 +624,20 @@ pub struct Arrow { use self::ArrowShape::*; -impl Arrow { - /// Return `true` if this is a default arrow. - fn is_default(&self) -> bool { - self.arrows.is_empty() - } - +impl Default for Arrow { /// Arrow constructor which returns a default arrow - pub fn default() -> Arrow { + fn default() -> Arrow { Arrow { arrows: vec![], } } +} + +impl Arrow { + /// Return `true` if this is a default arrow. + fn is_default(&self) -> bool { + self.arrows.is_empty() + } /// Arrow constructor which returns an empty arrow pub fn none() -> Arrow {