Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helper functions to deal with invalid fields #41

Merged
merged 5 commits into from
Feb 10, 2025

Conversation

lucatrv
Copy link
Contributor

@lucatrv lucatrv commented Feb 24, 2024

No description provided.

Copy link

coderabbitai bot commented Feb 24, 2024

Walkthrough

The update introduces modifications to the src/field_attributes.rs file, primarily focusing on the deserialization capabilities of various data types using Serde. Key changes include the removal of Serialize derives from several structs, the addition of new deserialization functions, refactoring existing functions to utilize the updated Deserialize trait, adjustments to a macro, and updates to documentation comments to reflect these changes. Additionally, the src/container_attributes.rs file has cosmetic changes to comments, and the src/serde_introspection.rs file simplifies the lifetime management in the Deserializer trait implementations.

Changes

Files Change Summary
src/field_attributes.rs - Removed serde::Serialize from multiple structs
- Added new functions: deserialize_to_type_or_fallback and deserialize_to_type_or_none
- Refactored deserialization functions to use the updated Deserialize trait
- Updated StringOrVecToVecParser macro
- Modified documentation comments for clarity and usage examples
src/container_attributes.rs - Adjusted formatting of comments in deserialize_struct_case_insensitive function
src/serde_introspection.rs - Removed lifetime parameter 'a from Deserializer implementations for StructFieldsDeserializer and EnumVariantsDeserializer

🐰✨
Changes abound, the code does refine,
Serialize gone, Deserialize shines.
Traits and derives, neatly align,
Helpers emerge, as stars do entwine.
Debug and equal, more robust by design,
A rabbit's work, in code, does entwine. 🌟📜
🐰✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abf4048 and ac3b478.

⛔ Files ignored due to path filters (1)
  • Cargo.toml is excluded by !**/*.toml
📒 Files selected for processing (1)
  • src/field_attributes.rs (35 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Test (Windows)
  • GitHub Check: Test (nightly)
  • GitHub Check: Test (beta)
  • GitHub Check: Test (stable)
  • GitHub Check: Run clippy
  • GitHub Check: MSRV
  • GitHub Check: Build docs
  • GitHub Check: Clippy
🔇 Additional comments (5)
src/field_attributes.rs (5)

623-624: Improved floating-point comparison accuracy.

The use of f64::EPSILON for floating-point comparisons is more accurate than direct equality comparison, as it accounts for floating-point arithmetic imprecision.

Also applies to: 643-644


829-885: LGTM! Well-designed fallback deserialization.

The function provides a clean way to attempt deserialization to a primary type with a fallback option, with clear documentation and examples.


887-923: LGTM! Clean implementation for handling invalid fields.

The function elegantly handles invalid fields by returning None instead of failing, which is a good pattern for optional fields.


925-987: Consider using serde-content instead of serde-value.

Based on the past review comments, there's an ongoing discussion about moving away from serde-value to newer alternatives like serde-content. While the implementation is correct, consider planning for this migration.


1136-1140: LGTM! Improved trait implementations.

The trait implementations are now more idiomatic and follow Rust best practices, particularly for the From and FromIterator traits.

Also applies to: 1192-1199, 1210-1217


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 6d0e9f0 and df9b28f.
Files ignored due to path filters (1)
  • Cargo.toml is excluded by: !**/*.toml
Files selected for processing (1)
  • src/field_attributes.rs (29 hunks)
Additional comments: 12
src/field_attributes.rs (12)
  • 286-292: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [264-317]

The function deserialize_number_from_string is well-implemented, leveraging Rust's type system and serde's capabilities to deserialize numbers from either string or numeric inputs. This function enhances the flexibility of input data handling, which is particularly useful in scenarios where the data source might not be consistent in its representation of numbers.

  • 337-343: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [340-397]

The function deserialize_option_number_from_string correctly handles optional numeric fields, allowing for a graceful handling of null, empty strings, or valid numeric values (both as strings and as numbers). This function is a valuable addition for APIs or data formats where optional fields may be omitted or presented in different formats.

  • 543-549: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [546-669]

The function deserialize_bool_from_anything introduces a robust way to deserialize boolean values from various input formats, including strings, integers, floats, and booleans. This function significantly enhances flexibility and error tolerance when dealing with diverse data sources. However, it's important to ensure that this flexibility aligns with the expected data integrity and validation requirements of the application.

  • 666-672: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [669-714]

The function deserialize_string_from_number provides a straightforward and useful functionality to deserialize numeric values as strings, which can be particularly useful in contexts where numeric values need to be treated as text, such as identifiers or codes. This function complements the library's deserialization capabilities well.

  • 711-717: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [714-747]

The function deserialize_default_from_null is a practical utility for handling null values by substituting them with default values of the specified type. This function can simplify handling optional fields in data structures, ensuring that they have sensible defaults when not explicitly provided.

  • 747-753: The function deserialize_default_from_empty_object introduces a nuanced approach to handling null or empty objects by defaulting to a specified type's default value. This function is particularly useful for complex nested structures where an empty or missing object should not lead to an error but rather a default state. It's a thoughtful addition to the library's deserialization utilities.
  • 802-808: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [805-821]

The function deserialize_vec_from_string_or_vec is a versatile utility that allows for deserializing a list of elements either from a comma-separated string or a JSON array. This function enhances the library's flexibility in handling list inputs, accommodating various data representation formats seamlessly.

  • 828-884: The function deserialize_to_type_or_fallback introduces an innovative approach to deserialization by attempting to deserialize to a primary type and falling back to a secondary type if the first attempt fails. This function can be particularly useful in scenarios where data might be represented in multiple valid formats, and the application needs to handle all such formats gracefully.
  • 886-922: The function deserialize_to_type_or_none provides a fail-safe deserialization mechanism where, instead of failing on incompatible types, it returns None. This approach is beneficial for optional fields where data integrity is not strictly enforced, and the absence of a value is an acceptable outcome.
  • 924-987: The function deserialize_to_type_or_string_lossy offers a flexible deserialization strategy that attempts to deserialize to a specified type and, upon failure, captures the input as a string. This function is particularly useful for logging or error reporting purposes, where understanding the original input can provide valuable context for debugging.
  • 994-1000: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [997-1014]

The macro StringOrVecToVecParser is a powerful tool for creating custom deserializers that can handle both string and vector inputs, converting them into a vector of a specified type. This macro significantly enhances the library's deserialization capabilities, allowing for custom parsing logic that can accommodate complex input scenarios.

  • 1116-1122: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [1071-1169]

The StringOrVecToVec struct and its associated methods provide a comprehensive solution for parsing strings or vectors into vectors of a specified type. This functionality is crucial for handling data that may come in different formats but needs to be normalized into a consistent internal representation. The design of this utility is well-thought-out, offering flexibility through custom separators and parsing functions.

@lucatrv lucatrv force-pushed the add_field_helper_functions branch from df9b28f to 08ebb38 Compare February 24, 2024 22:06
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 6d0e9f0 and 08ebb38.
Files ignored due to path filters (1)
  • Cargo.toml is excluded by: !**/*.toml
Files selected for processing (1)
  • src/field_attributes.rs (29 hunks)
Files skipped from review as they are similar to previous changes (1)
  • src/field_attributes.rs

@lucatrv lucatrv force-pushed the add_field_helper_functions branch from 08ebb38 to e01ff98 Compare February 24, 2024 22:52
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 6d0e9f0 and e01ff98.
Files ignored due to path filters (1)
  • Cargo.toml is excluded by: !**/*.toml
Files selected for processing (1)
  • src/field_attributes.rs (29 hunks)
Files skipped from review as they are similar to previous changes (1)
  • src/field_attributes.rs

src/field_attributes.rs Show resolved Hide resolved
src/field_attributes.rs Show resolved Hide resolved
src/field_attributes.rs Show resolved Hide resolved
src/field_attributes.rs Outdated Show resolved Hide resolved
src/field_attributes.rs Show resolved Hide resolved
src/field_attributes.rs Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
src/field_attributes.rs (1)

Line range hint 606-645: Replace legacy EPSILON constant with f64::EPSILON.

Update the code to use the associated constant from the primitive type instead of the legacy numeric constant.

-use std::f64::EPSILON;

 match AnythingOrBool::deserialize(deserializer)? {
     AnythingOrBool::Boolean(b) => Ok(b),
     AnythingOrBool::Int(i) => match i {
         1 => Ok(true),
         0 => Ok(false),
         _ => Err(serde::de::Error::custom("The number is neither 1 nor 0")),
     },
     AnythingOrBool::Float(f) => {
-        if (f - 1.0f64).abs() < EPSILON {
+        if (f - 1.0f64).abs() < f64::EPSILON {
             Ok(true)
         } else if f == 0.0f64 {
             Ok(false)
         } else {
             Err(serde::de::Error::custom(
                 "The number is neither 1.0 nor 0.0",
             ))
         }
     },
🧹 Nitpick comments (2)
src/field_attributes.rs (2)

Line range hint 1139-1427: Elide unnecessary explicit lifetimes.

Several implementations have explicit lifetimes that could be elided to improve code readability.

-impl<'a> From<char> for Pattern<'a> {
+impl From<char> for Pattern<'_> {
     fn from(c: char) -> Self {
         Pattern::Char(c)
     }
 }

-impl<'a> From<&'a str> for Pattern<'a> {
+impl From<&str> for Pattern<'_> {
     fn from(s: &str) -> Self {
         Pattern::Str(s)
     }
 }

-impl<'a> From<Vec<Pattern<'a>>> for Pattern<'a> {
+impl From<Vec<Pattern<'_>>> for Pattern<'_> {
     fn from(patterns: Vec<Pattern>) -> Self {
         Pattern::Multiple(patterns)
     }
 }

-impl<'a> std::iter::FromIterator<Pattern<'a>> for Pattern<'a> {
+impl std::iter::FromIterator<Pattern<'_>> for Pattern<'_> {
     fn from_iter<I>(iter: I) -> Self
     where
         I: IntoIterator<Item = Pattern>,
     {
         Pattern::Multiple(iter.into_iter().collect())
     }
 }

-impl<'a, 'de, T> Default for StringOrVecToVec<'a, T, T::Err>
+impl<T> Default for StringOrVecToVec<'_, T, T::Err>
 where
     T: FromStr + Deserialize<'de> + 'static,
     <T as FromStr>::Err: std::fmt::Display,
 {
     fn default() -> Self {
         Self::new(|c| c == ',', T::from_str, false)
     }
 }

-impl<'a> Pattern<'a> {
+impl Pattern<'_> {
     fn split(&self, input: &str) -> Vec<&str> {
         // ...
     }
 }

Line range hint 1-1500: Overall, the changes enhance the library's deserialization capabilities.

The new helper functions provide flexible deserialization options with comprehensive documentation. While there are opportunities for improvement (custom Either enum, serde_json::Value usage, lifetime elision), the implementation is solid and functional.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e01ff98 and 184fbd1.

⛔ Files ignored due to path filters (1)
  • Cargo.toml is excluded by !**/*.toml
📒 Files selected for processing (1)
  • src/field_attributes.rs (29 hunks)
🧰 Additional context used
🪛 GitHub Actions: Lint
src/field_attributes.rs

[error] 606-606: Importing a legacy numeric constant. Use the associated constant f64::EPSILON from the primitive type instead.


[error] 625-625: Usage of legacy numeric constant EPSILON. Use f64::EPSILON instead.


[error] 645-645: Usage of legacy numeric constant EPSILON. Use f64::EPSILON instead.


[error] 1139-1139: Explicit lifetimes could be elided in Pattern implementation.


[error] 1195-1195: Explicit lifetimes could be elided in FromIterator implementation.


[error] 1213-1213: Explicit lifetimes could be elided in From implementation.


[error] 1222-1222: Explicit lifetimes could be elided in Default implementation for StringOrVecToVec.


[error] 1427-1427: Explicit lifetimes could be elided in Pattern implementation.

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Test (Windows)
  • GitHub Check: Test (nightly)
  • GitHub Check: Test (beta)
  • GitHub Check: Test (stable)
  • GitHub Check: MSRV
  • GitHub Check: Build docs
🔇 Additional comments (5)
src/field_attributes.rs (5)

Line range hint 255-308: LGTM! The trait bounds and documentation improvements enhance type safety and usability.

The updated trait bounds and comprehensive examples effectively demonstrate both string and direct number deserialization, including usage with custom types.


831-887: Consider using a custom Either enum for better semantics.

While the current implementation using an untagged enum is efficient, a custom Either enum would make the intent more explicit and provide better context.

+pub enum Either<T, F> {
+    Primary(T),
+    Fallback(F),
+}

 pub fn deserialize_to_type_or_fallback<'de, D, T, F>(
     deserializer: D,
 ) -> Result<Result<T, F>, D::Error>
 where
     D: Deserializer<'de>,
     T: Deserialize<'de>,
     F: Deserialize<'de>,
 {
-    #[derive(Deserialize)]
-    #[serde(untagged)]
-    enum DeEither<T, F> {
-        Type(T),
-        Fallback(F),
-    }
-
-    DeEither::<T, F>::deserialize(deserializer).map(|de| match de {
-        DeEither::Type(t) => Ok(t),
-        DeEither::Fallback(f) => Err(f),
+    Either::<T, F>::deserialize(deserializer).map(|either| match either {
+        Either::Primary(t) => Ok(t),
+        Either::Fallback(f) => Err(f),
     })
 }

889-925: LGTM! Clean implementation with clear documentation.

The function provides a useful way to handle deserialization failures gracefully by returning None instead of failing.


927-990: Consider using serde_json::Value instead of serde-value.

The current implementation relies on the serde-value crate, which might be less maintained. Using serde_json::Value would reduce dependencies while achieving the same result.

 pub fn deserialize_to_type_or_string_lossy<'de, D, T>(
     deserializer: D,
 ) -> Result<Result<T, String>, D::Error>
 where
     D: Deserializer<'de>,
     T: Deserialize<'de>,
 {
-    let value = serde_value::Value::deserialize(deserializer)?;
+    let value = serde_json::Value::deserialize(deserializer)?;
     let result = T::deserialize(value.clone()).map_err(|_| match value {
-        serde_value::Value::Bool(b) => b.to_string(),
-        serde_value::Value::U8(u) => u.to_string(),
-        serde_value::Value::U16(u) => u.to_string(),
-        serde_value::Value::U32(u) => u.to_string(),
-        serde_value::Value::U64(u) => u.to_string(),
-        serde_value::Value::I8(i) => i.to_string(),
-        serde_value::Value::I16(i) => i.to_string(),
-        serde_value::Value::I32(i) => i.to_string(),
-        serde_value::Value::I64(i) => i.to_string(),
-        serde_value::Value::F32(f) => f.to_string(),
-        serde_value::Value::F64(f) => f.to_string(),
-        serde_value::Value::Char(c) => c.to_string(),
-        serde_value::Value::String(s) => s,
-        serde_value::Value::Unit => String::new(),
-        serde_value::Value::Option(opt) => format!("{:?}", opt),
-        serde_value::Value::Newtype(nt) => format!("{:?}", nt),
-        serde_value::Value::Seq(seq) => format!("{:?}", seq),
-        serde_value::Value::Map(map) => format!("{:?}", map),
-        serde_value::Value::Bytes(v) => String::from_utf8_lossy(&v).into_owned(),
+        serde_json::Value::Null => String::new(),
+        serde_json::Value::Bool(b) => b.to_string(),
+        serde_json::Value::Number(n) => n.to_string(),
+        serde_json::Value::String(s) => s,
+        serde_json::Value::Array(a) => format!("{:?}", a),
+        serde_json::Value::Object(o) => format!("{:?}", o),
     });
     Ok(result)
 }

1389-1393: Verify the necessity of explicit lifetime bounds.

Could you clarify why these lifetime bounds were necessary? They might be candidates for elision if they're not required for resolving ambiguity.

✅ Verification successful

Lifetime bounds are necessary and correct

The explicit lifetime bound 'a: 'de is required because the struct StringOrVecToVec has a lifetime parameter 'a (from Pattern<'a>), and this lifetime must outlive the deserialization lifetime 'de to ensure the borrowed data remains valid throughout the deserialization process.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any lifetime-related compiler errors without these bounds
cat > /tmp/test.rs << 'EOL'
use serde::Deserialize;
pub struct StringOrVecToVec<T, E> {}
impl<T, E> StringOrVecToVec<T, E> {
    pub fn into_deserializer<D>(
        self,
    ) -> impl FnMut(D) -> Result<Vec<T>, D::Error>
    where
        D: Deserializer,
        T: Deserialize,
        E: std::fmt::Display,
    {
        unimplemented!()
    }
}
EOL
rustc --edition=2021 /tmp/test.rs

Length of output: 138


Script:

#!/bin/bash
# Get the full context of the function and surrounding code
rg -B 10 -A 10 "into_deserializer.*where" src/field_attributes.rs

# Look for similar lifetime patterns
ast-grep --pattern 'impl$$$where$$$\bwhere\s+'\''a:\s*'\''de'

Length of output: 131


Script:

#!/bin/bash
# Get the struct definition and implementation
rg -A 5 "^pub struct StringOrVecToVec" src/field_attributes.rs

# Find the specific function implementation
rg -B 5 -A 15 "fn into_deserializer.*FnMut.*Result.*Vec" src/field_attributes.rs

# Look for other deserializer implementations
rg "impl.*Deserialize.*for" src/field_attributes.rs

Length of output: 336

@lucatrv lucatrv changed the title feat(field_attributes): add helper functions add helper functions to deal with invalid fields Jan 27, 2025
@lucatrv lucatrv changed the title add helper functions to deal with invalid fields Add helper functions to deal with invalid fields Jan 27, 2025
@lucatrv lucatrv force-pushed the add_field_helper_functions branch from 184fbd1 to 2ea3a23 Compare January 27, 2025 13:52
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/field_attributes.rs (1)

Line range hint 620-626: Improve floating-point comparison precision.

The current floating-point comparison using f64::EPSILON might not be precise enough for all cases. Consider using a more appropriate epsilon value or a relative comparison method.

-if (f - 1.0f64).abs() < f64::EPSILON {
+const EPSILON: f64 = 1e-10;
+if (f - 1.0f64).abs() < EPSILON {

Also applies to: 640-646

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 184fbd1 and 2ea3a23.

⛔ Files ignored due to path filters (1)
  • Cargo.toml is excluded by !**/*.toml
📒 Files selected for processing (3)
  • src/container_attributes.rs (1 hunks)
  • src/field_attributes.rs (35 hunks)
  • src/serde_introspection.rs (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/container_attributes.rs
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Test (Windows)
  • GitHub Check: Run clippy
  • GitHub Check: Clippy
🔇 Additional comments (5)
src/serde_introspection.rs (1)

43-43: LGTM! Good simplification of lifetime parameters.

The changes simplify lifetime management by using wildcard lifetimes ('_) instead of explicit lifetime parameters. This makes the code more concise while maintaining the same functionality.

Also applies to: 77-77

src/field_attributes.rs (4)

317-318: LGTM! Consistent trait bounds.

The trait bounds are now more explicit and consistent across functions, properly specifying the Deserialize<'de> requirement.

Also applies to: 397-398, 428-429


826-882: Consider using a custom enum instead of Result.

While the implementation is correct, using a custom enum like Either<T, F> would better express the intent and provide more semantic clarity than Result<T, F>.

+enum Either<T, F> {
+    First(T),
+    Second(F),
+}

-pub fn deserialize_to_type_or_fallback<'de, D, T, F>(
-    deserializer: D,
-) -> Result<Result<T, F>, D::Error>
+pub fn deserialize_to_type_or_fallback<'de, D, T, F>(
+    deserializer: D,
+) -> Result<Either<T, F>, D::Error>

884-920: LGTM! Clean implementation for handling invalid fields.

The function provides a simple and effective way to handle invalid fields by returning None instead of failing.


922-985: Consider using serde_json::Value instead of serde_value.

The implementation could benefit from using serde_json::Value instead of relying on the potentially unmaintained serde-value crate.

@lucatrv lucatrv force-pushed the add_field_helper_functions branch from bbb376d to 70a2b91 Compare January 27, 2025 22:43
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/field_attributes.rs (1)

954-988: 🛠️ Refactor suggestion

Consider alternatives to serde-value dependency.

As noted in previous reviews, using the serde-value crate introduces an additional dependency that might not be necessary. Consider using serde_json::Value instead, which is more commonly used and likely to be maintained.

 pub fn deserialize_to_type_or_string_lossy<'de, D, T>(
     deserializer: D,
 ) -> Result<Result<T, String>, D::Error>
 where
     D: Deserializer<'de>,
     T: Deserialize<'de>,
 {
-    let value = serde_value::Value::deserialize(deserializer)?;
-    let result = T::deserialize(value.clone()).map_err(|_| match value {
-        serde_value::Value::Bool(b) => b.to_string(),
-        serde_value::Value::U8(u) => u.to_string(),
-        // ... more matches
-        serde_value::Value::Bytes(v) => String::from_utf8_lossy(&v).into_owned(),
-    });
+    let value = serde_json::Value::deserialize(deserializer)?;
+    T::deserialize(value.clone())
+        .map(Ok)
+        .unwrap_or_else(|_| Ok(Err(value.to_string())))
 }
🧹 Nitpick comments (2)
src/field_attributes.rs (2)

917-923: Consider adding error logging.

While the implementation is correct, consider logging the deserialization error before returning None. This would help with debugging in production environments.

 pub fn deserialize_to_type_or_none<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
 where
     D: Deserializer<'de>,
     Option<T>: Deserialize<'de>,
 {
-    Option::<T>::deserialize(deserializer).or_else(|_| Ok(None))
+    Option::<T>::deserialize(deserializer).or_else(|e| {
+        log::debug!("Failed to deserialize value: {}", e);
+        Ok(None)
+    })
 }

Line range hint 1425-1453: Optimize Pattern::split implementation.

The current implementation could be improved in terms of performance and readability:

  1. Consider pre-allocating the Vec with a capacity hint
  2. Use more descriptive variable names than delete_until
  3. Consider using iterators instead of explicit Vec operations
 impl Pattern<'_> {
     fn split<'b>(&self, input: &'b str) -> Vec<&'b str> {
         match self {
             Pattern::Char(c) => input.split(*c).collect(),
             Pattern::Str(s) => input.split(s).collect(),
             Pattern::Pred(p) => input.split(p).collect(),
             Pattern::Multiple(patterns) => {
-                let mut split = vec![input];
+                let estimated_capacity = patterns.len() * 2;
+                let mut current_segments = Vec::with_capacity(estimated_capacity);
+                current_segments.push(input);
+
                 for pattern in patterns {
-                    let delete_until = split.len();
-                    let mut new_split = Vec::new();
-                    for s in &split {
-                        new_split.append(&mut pattern.split(s));
-                    }
-
-                    if !new_split.is_empty() {
-                        split = split.split_off(delete_until);
-                    }
-
-                    split.append(&mut new_split);
+                    let next_segments: Vec<_> = current_segments
+                        .iter()
+                        .flat_map(|&s| pattern.split(s))
+                        .collect();
+                    
+                    if !next_segments.is_empty() {
+                        current_segments = next_segments;
+                    }
                 }
-                split
+                current_segments
             }
         }
     }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bbb376d and 70a2b91.

⛔ Files ignored due to path filters (1)
  • Cargo.toml is excluded by !**/*.toml
📒 Files selected for processing (3)
  • src/container_attributes.rs (1 hunks)
  • src/field_attributes.rs (35 hunks)
  • src/serde_introspection.rs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/container_attributes.rs
  • src/serde_introspection.rs
🔇 Additional comments (3)
src/field_attributes.rs (3)

308-309: LGTM! Improved trait bounds.

The trait bounds are now more explicit and correct by including Deserialize<'de>.


866-885: LGTM! Clean implementation of type fallback.

The implementation is elegant and well-documented. The use of an untagged enum for handling the fallback type is a clean approach.


1220-1224: LGTM! Improved trait bounds for Default implementation.

The trait bounds are now more explicit and correct by including Deserialize<'de>.

@lucatrv lucatrv force-pushed the add_field_helper_functions branch from 70a2b91 to abf4048 Compare January 30, 2025 17:35
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/field_attributes.rs (2)

925-988: Consider optimizing the error mapping logic.

While the implementation is thorough, the error mapping could be simplified by using a match expression directly in the map_err closure, as suggested in a previous review.

Apply this optimization:

-    let result = T::deserialize(value.clone()).map_err(|_| match value {
-        serde_value::Value::Bool(b) => b.to_string(),
-        serde_value::Value::U8(u) => u.to_string(),
-        serde_value::Value::U16(u) => u.to_string(),
-        serde_value::Value::U32(u) => u.to_string(),
-        serde_value::Value::U64(u) => u.to_string(),
-        serde_value::Value::I8(i) => i.to_string(),
-        serde_value::Value::I16(i) => i.to_string(),
-        serde_value::Value::I32(i) => i.to_string(),
-        serde_value::Value::I64(i) => i.to_string(),
-        serde_value::Value::F32(f) => f.to_string(),
-        serde_value::Value::F64(f) => f.to_string(),
-        serde_value::Value::Char(c) => c.to_string(),
-        serde_value::Value::String(s) => s,
-        serde_value::Value::Unit => String::new(),
-        serde_value::Value::Option(opt) => {
-            format!("{:?}", opt)
-        }
-        serde_value::Value::Newtype(nt) => {
-            format!("{:?}", nt)
-        }
-        serde_value::Value::Seq(seq) => format!("{:?}", seq),
-        serde_value::Value::Map(map) => format!("{:?}", map),
-        serde_value::Value::Bytes(v) => String::from_utf8_lossy(&v).into_owned(),
-    });
+    T::deserialize(value.clone()).map_err(|_| match value {
+        serde_value::Value::Bool(b) => b.to_string(),
+        serde_value::Value::U8(u) => u.to_string(),
+        serde_value::Value::U16(u) => u.to_string(),
+        serde_value::Value::U32(u) => u.to_string(),
+        serde_value::Value::U64(u) => u.to_string(),
+        serde_value::Value::I8(i) => i.to_string(),
+        serde_value::Value::I16(i) => i.to_string(),
+        serde_value::Value::I32(i) => i.to_string(),
+        serde_value::Value::I64(i) => i.to_string(),
+        serde_value::Value::F32(f) => f.to_string(),
+        serde_value::Value::F64(f) => f.to_string(),
+        serde_value::Value::Char(c) => c.to_string(),
+        serde_value::Value::String(s) => s,
+        serde_value::Value::Unit => String::new(),
+        serde_value::Value::Option(opt) => format!("{:?}", opt),
+        serde_value::Value::Newtype(nt) => format!("{:?}", nt),
+        serde_value::Value::Seq(seq) => format!("{:?}", seq),
+        serde_value::Value::Map(map) => format!("{:?}", map),
+        serde_value::Value::Bytes(v) => String::from_utf8_lossy(&v).into_owned(),
+    })

Line range hint 1425-1453: Consider optimizing the multiple pattern splitting logic.

While the implementation is correct, the recursive approach for handling multiple patterns could be optimized to avoid unnecessary allocations and copies.

Consider these optimizations:

  1. Pre-allocate the result vector with a capacity hint
  2. Use iterators more extensively to avoid intermediate allocations
  3. Consider implementing a custom iterator for pattern matching

Example optimization for the Multiple case:

Pattern::Multiple(patterns) => {
    let mut result = Vec::with_capacity(input.len() / 2); // Capacity hint
    let mut current = vec![input];
    
    for pattern in patterns {
        current = current.into_iter()
            .flat_map(|s| pattern.split(s))
            .collect();
    }
    
    result.extend(current);
    result
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70a2b91 and abf4048.

⛔ Files ignored due to path filters (1)
  • Cargo.toml is excluded by !**/*.toml
📒 Files selected for processing (3)
  • src/container_attributes.rs (1 hunks)
  • src/field_attributes.rs (35 hunks)
  • src/serde_introspection.rs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/container_attributes.rs
  • src/serde_introspection.rs
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Test (nightly)
  • GitHub Check: Build docs
🔇 Additional comments (6)
src/field_attributes.rs (6)

Line range hint 255-308: LGTM! Well-documented and correctly implemented.

The function signature update to include Deserialize<'de> trait bound is appropriate, and the implementation correctly handles both string and numeric deserialization cases.


Line range hint 331-388: LGTM! Comprehensive test coverage and robust implementation.

The function handles all edge cases correctly and has excellent test coverage for various scenarios including null values, empty strings, and invalid inputs.


829-885: LGTM! Elegant implementation for handling type fallbacks.

The new function provides a clean solution for handling fields that could be of different types, with clear examples demonstrating practical use cases like i64/f64 and f64/String combinations.


887-923: LGTM! Clean implementation for handling invalid fields.

The new function provides a simple yet effective way to handle invalid fields by returning None instead of failing, which is particularly useful for partial data processing.


Line range hint 1137-1219: LGTM! Comprehensive and flexible Pattern implementations.

The various From implementations for the Pattern enum provide good flexibility for pattern matching, with clear documentation and practical examples.


Line range hint 1220-1234: LGTM! Appropriate trait bounds and implementation.

The Default implementation for StringOrVecToVec correctly uses comma as the default separator and includes appropriate trait bounds.

@lucatrv
Copy link
Contributor Author

lucatrv commented Jan 30, 2025

@iddm I think I replied to all your points, please let me know if anything else is needed.

Copy link
Owner

@iddm iddm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again! Just one little minor comment and we are good to go!

@lucatrv lucatrv force-pushed the add_field_helper_functions branch from abf4048 to ac3b478 Compare February 10, 2025 20:17
@iddm iddm merged commit 72c7081 into iddm:master Feb 10, 2025
10 checks passed
@iddm
Copy link
Owner

iddm commented Feb 10, 2025

Here it is: https://crates.io/crates/serde-aux/4.6.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants