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

Do various improvements #23

Merged
merged 9 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 131 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,140 @@ keywords = ["ord", "partialord", "wrapper"]
readme = "README.md"
edition = "2021"
rust-version = "1.63.0"
exclude = ["justfile", "rustfmt.toml", "clippy.toml"]
exclude = ["tests", "contrib"]

[features]

[dependencies]

[features]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[[example]]
name = "point"

[lints.clippy]
# Exhaustive list of pedantic clippy lints
assigning_clones = "warn"
bool_to_int_with_if = "warn"
borrow_as_ptr = "warn"
case_sensitive_file_extension_comparisons = "warn"
cast_lossless = "warn"
cast_possible_truncation = "warn"
cast_possible_wrap = "warn"
cast_precision_loss = "warn"
cast_ptr_alignment = "warn"
cast_sign_loss = "warn"
checked_conversions = "warn"
cloned_instead_of_copied = "warn"
copy_iterator = "warn"
default_trait_access = "warn"
doc_link_with_quotes = "warn"
doc_markdown = "warn"
empty_enum = "warn"
enum_glob_use = "warn"
expl_impl_clone_on_copy = "warn"
explicit_deref_methods = "warn"
explicit_into_iter_loop = "warn"
explicit_iter_loop = "warn"
filter_map_next = "warn"
flat_map_option = "warn"
float_cmp = "warn"
fn_params_excessive_bools = "warn"
from_iter_instead_of_collect = "warn"
if_not_else = "warn"
ignored_unit_patterns = "warn"
implicit_clone = "warn"
implicit_hasher = "warn"
inconsistent_struct_constructor = "warn"
index_refutable_slice = "warn"
inefficient_to_string = "warn"
inline_always = "warn"
into_iter_without_iter = "warn"
invalid_upcast_comparisons = "warn"
items_after_statements = "warn"
iter_filter_is_ok = "warn"
iter_filter_is_some = "warn"
iter_not_returning_iterator = "warn"
iter_without_into_iter = "warn"
large_digit_groups = "warn"
large_futures = "warn"
large_stack_arrays = "warn"
large_types_passed_by_value = "warn"
linkedlist = "warn"
macro_use_imports = "warn"
manual_assert = "warn"
manual_instant_elapsed = "warn"
manual_is_power_of_two = "warn"
manual_is_variant_and = "warn"
manual_let_else = "warn"
manual_ok_or = "warn"
manual_string_new = "warn"
many_single_char_names = "warn"
map_unwrap_or = "warn"
match_bool = "warn"
match_on_vec_items = "warn"
match_same_arms = "warn"
match_wild_err_arm = "warn"
match_wildcard_for_single_variants = "warn"
maybe_infinite_iter = "warn"
mismatching_type_param_order = "warn"
missing_errors_doc = "warn"
missing_fields_in_debug = "warn"
missing_panics_doc = "warn"
must_use_candidate = "warn"
mut_mut = "warn"
naive_bytecount = "warn"
needless_bitwise_bool = "warn"
needless_continue = "warn"
needless_for_each = "warn"
needless_pass_by_value = "warn"
needless_raw_string_hashes = "warn"
no_effect_underscore_binding = "warn"
no_mangle_with_rust_abi = "warn"
option_as_ref_cloned = "warn"
option_option = "warn"
ptr_as_ptr = "warn"
ptr_cast_constness = "warn"
pub_underscore_fields = "warn"
range_minus_one = "warn"
range_plus_one = "warn"
redundant_closure_for_method_calls = "warn"
redundant_else = "warn"
ref_as_ptr = "warn"
ref_binding_to_reference = "warn"
ref_option = "warn"
ref_option_ref = "warn"
return_self_not_must_use = "warn"
same_functions_in_if_condition = "warn"
semicolon_if_nothing_returned = "warn"
should_panic_without_expect = "warn"
similar_names = "warn"
single_char_pattern = "warn"
single_match_else = "warn"
stable_sort_primitive = "warn"
str_split_at_newline = "warn"
string_add_assign = "warn"
struct_excessive_bools = "warn"
struct_field_names = "warn"
too_many_lines = "warn"
transmute_ptr_to_ptr = "warn"
trivially_copy_pass_by_ref = "warn"
unchecked_duration_subtraction = "warn"
unicode_not_nfc = "warn"
uninlined_format_args = "allow" # This is a subjective style choice.
unnecessary_box_returns = "warn"
unnecessary_join = "warn"
unnecessary_literal_bound = "warn"
unnecessary_wraps = "warn"
unnested_or_patterns = "warn"
unreadable_literal = "warn"
unsafe_derive_deserialize = "warn"
unused_async = "warn"
unused_self = "warn"
used_underscore_binding = "warn"
used_underscore_items = "warn"
verbose_bit_mask = "warn"
wildcard_imports = "warn"
zero_sized_map_values = "warn"
4 changes: 1 addition & 3 deletions examples/point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ fn main() {

println!();
println!("Looking in map for key: {}", a);
let found = map
.get(Ordered::from_ref(&a))
.expect("failed to look up key");
let found = map.get(Ordered::from_ref(&a)).expect("failed to look up key");
println!("Found it, with value: {}", found);
}

Expand Down
36 changes: 20 additions & 16 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
max_width = 100
ignore = []
hard_tabs = false
tab_spaces = 4
newline_style = "Auto"
indent_style = "Block"
use_small_heuristics = "Default"
fn_call_width = 80
attr_fn_like_width = 70
struct_lit_width = 80
struct_variant_width = 35
array_width = 60
chain_width = 60
single_line_if_else_max_width = 50

max_width = 100 # This is number of characters.
# `use_small_heuristics` is ignored if the granular width config values are explicitly set.
use_small_heuristics = "Max" # "Max" == All granular width settings same as `max_width`.
# # Granular width configuration settings. These are percentages of `max_width`.
# fn_call_width = 60
# attr_fn_like_width = 70
# struct_lit_width = 18
# struct_variant_width = 35
# array_width = 60
# chain_width = 60
# single_line_if_else_max_width = 50

wrap_comments = false
format_code_in_doc_comments = false
comment_width = 80
comment_width = 100 # Default 80
normalize_comments = false
normalize_doc_attributes = false
format_strings = false
Expand All @@ -22,7 +27,7 @@ format_macro_bodies = true
hex_literal_case = "Preserve"
empty_item_single_line = true
struct_lit_single_line = true
fn_single_line = true
fn_single_line = true # Default false
where_single_line = false
imports_indent = "Block"
imports_layout = "Mixed"
Expand All @@ -41,7 +46,7 @@ combine_control_expr = true
overflow_delimited_expr = false
struct_field_align_threshold = 0
enum_discrim_align_threshold = 0
match_arm_blocks = true
match_arm_blocks = false # Default true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
fn_params_layout = "Tall"
Expand All @@ -52,8 +57,8 @@ trailing_comma = "Vertical"
match_block_trailing_comma = false
blank_lines_upper_bound = 1
blank_lines_lower_bound = 0
edition = "2018"
version = "One"
edition = "2021"
style_edition = "2021"
inline_attribute_width = 0
format_generated_files = true
merge_derives = true
Expand All @@ -65,9 +70,8 @@ color = "Auto"
unstable_features = false
disable_all_formatting = false
skip_children = false
hide_parse_errors = false
show_parse_errors = true
error_on_line_overflow = false
error_on_unformatted = false
ignore = []
emit_mode = "Files"
make_backup = false
78 changes: 69 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,50 @@
//! Provides a wrapper for types that can technically implement `PartialOrd`/`Ord`
//! but for semantic reasons it is nonsensical.
//!
//! For examples see the docs on [`Ordered`] or the code in `examples/point.rs`.
//! # Examples
//!
//! ```
//! # #![allow(unused)] // Because of `Adt`.
//! use core::{cmp::Ordering, fmt};
//! use ordered::{ArbitraryOrd, Ordered};
//!
//! /// A point in 2D space.
//! ///
//! /// We do not want users to be able to write `a < b` because it is not well defined.
//! #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
//! struct Point {
//! x: u32,
//! y: u32,
//! }
//!
//! impl ArbitraryOrd for Point {
//! fn arbitrary_cmp(&self, other: &Self) -> Ordering {
//! // Just use whatever order tuple cmp gives us.
//! (self.x, self.y).cmp(&(other.x, other.y))
//! }
//! }
//!
//! impl fmt::Display for Point {
//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//! write!(f, "({}, {})", self.x, self.y)
//! }
//! }
//!
//! /// `Ordered` allows users to derive `PartialOrd` on types that include a `Point`.
//! #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
//! struct Adt {
//! name: String,
//! point: Ordered<Point>,
//! }
//! ```

#![no_std]
// Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
// Coding conventions.
#![warn(missing_docs)]
#![warn(deprecated_in_future)]
#![doc(test(attr(warn(unused))))]

use core::borrow::{Borrow, BorrowMut};
use core::cmp::Ordering;
Expand All @@ -30,6 +67,7 @@ pub trait ArbitraryOrd: Eq + PartialEq {
/// # Examples
///
/// ```
/// # #![allow(unused)] // Because of `Adt`.
/// use core::{cmp::Ordering, fmt};
/// use ordered::{ArbitraryOrd, Ordered};
///
Expand Down Expand Up @@ -58,9 +96,8 @@ pub trait ArbitraryOrd: Eq + PartialEq {
/// let point = Point { x: 0, y: 1 };
/// let ordered = Ordered(point);
///
/// println!("We can explicitly deref (*ordered): {}", *ordered);
/// println!("Or use deref coercion (ordered): {}", ordered);
/// println!("Or we can use borrow (&ordered): {}", &ordered);
/// assert_eq!(*ordered, ordered.into_inner()); // Explicitly deref or use `into_inner()`.
/// assert_eq!(&ordered.0, ordered.as_ref()); // Use `AsRef` or `as_ref()`.
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
Expand All @@ -74,6 +111,12 @@ impl<T> Ordered<T> {
/// The inner type is public so this function is never explicitly needed.
pub const fn new(inner: T) -> Self { Self(inner) }

/// Creates an `Ordered<T>` from a reference.
///
/// This allows: `let found = map.get(Ordered::from_ref(&a));`
#[allow(clippy::ptr_as_ptr)]
pub fn from_ref(value: &T) -> &Self { unsafe { &*(value as *const _ as *const Self) } }

/// Returns a reference to the inner object.
///
/// We also implement [`core::borrow::Borrow`] so this function is never explicitly needed.
Expand All @@ -83,11 +126,6 @@ impl<T> Ordered<T> {
///
/// We also implement [`core::ops::Deref`] so this function is never explicitly needed.
pub fn into_inner(self) -> T { self.0 }

/// Creates an `Ordered<T>` from a reference.
///
/// This allows: `let found = map.get(Ordered::from_ref(&a));`
pub fn from_ref(value: &T) -> &Self { unsafe { &*(value as *const _ as *const Self) } }
}

impl<T: ArbitraryOrd> ArbitraryOrd for &T {
Expand Down Expand Up @@ -179,4 +217,26 @@ mod tests {

assert!(Ordered(&a) < Ordered(&b));
}

// Copied from https://rust-lang.github.io/api-guidelines/interoperability.html#c-send-sync
#[test]
fn send() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Point {
x: u32,
y: u32,
}

impl ArbitraryOrd for Point {
fn arbitrary_cmp(&self, other: &Self) -> Ordering {
(self.x, self.y).cmp(&(other.x, other.y))
}
}

fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}

assert_send::<Ordered<Point>>();
assert_sync::<Ordered<Point>>();
}
}
Loading