Repository: toml-rs/toml (rust)

Issue:
Array::sort_by_key doesn't work with Value::as_* methods
The new `Array::sort_by_key` doesn't seem to work when combined with the `Value::as_*` methods that the crate provides, like `Value::as_str`. Since I'm guessing that's the most common way to use that sorting function, I'd expect this test below to work, but it doesn't compile:

```rust
    #[test]
    fn sorting_with_references() {
        let values = vec!["foo", "qux", "bar"];
        let mut array = Array::from_iter(values);
        array.sort_by_key(Value::as_str);
    }
```


Compilation error:

```
error[E0308]: mismatched types
   --> crates/toml_edit/src/array.rs:433:9
    |
433 |         array.sort_by_key(Value::as_str);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
    |
    = note: expected enum `Option<&str>`
               found enum `Option<&str>`
note: the lifetime requirement is introduced here
   --> crates/toml_edit/src/array.rs:327:29
    |
327 |         F: FnMut(&Value) -> K,
    |                             ^

For more information about this error, try `rustc --explain E0308`.
```

Pull request description:
feat: Add Array::sort_by
`Array::sort_by_key` can't really use a borrowed key because it will be shuffling things around.  As an alternative, we can just provide the `cmp` to use.

Fixes #639

Patch:
diff --git a/crates/toml_edit/src/array.rs b/crates/toml_edit/src/array.rs
index bd9ac514..97033de5 100644
--- a/crates/toml_edit/src/array.rs
+++ b/crates/toml_edit/src/array.rs
@@ -317,6 +317,36 @@ impl Array {
             .retain(|item| item.as_value().map(&mut keep).unwrap_or(false));
     }
 
+    /// Sorts the slice with a comparator function.
+    ///
+    /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) worst-case.
+    ///
+    /// The comparator function must define a total ordering for the elements in the slice. If
+    /// the ordering is not total, the order of the elements is unspecified. An order is a
+    /// total order if it is (for all `a`, `b` and `c`):
+    ///
+    /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and
+    /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
+    ///
+    /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use
+    /// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`.
+    #[inline]
+    pub fn sort_by<F>(&mut self, mut compare: F)
+    where
+        F: FnMut(&Value, &Value) -> std::cmp::Ordering,
+    {
+        self.values.sort_by(move |lhs, rhs| {
+            let lhs = lhs.as_value();
+            let rhs = rhs.as_value();
+            match (lhs, rhs) {
+                (None, None) => std::cmp::Ordering::Equal,
+                (Some(_), None) => std::cmp::Ordering::Greater,
+                (None, Some(_)) => std::cmp::Ordering::Less,
+                (Some(lhs), Some(rhs)) => compare(lhs, rhs),
+            }
+        })
+    }
+
     /// Sorts the array with a key extraction function.
     ///
     /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*))


Test patch:
diff --git a/crates/toml_edit/tests/testsuite/edit.rs b/crates/toml_edit/tests/testsuite/edit.rs
index 28f73c1a..94c20d41 100644
--- a/crates/toml_edit/tests/testsuite/edit.rs
+++ b/crates/toml_edit/tests/testsuite/edit.rs
@@ -853,3 +853,10 @@ src.git = "https://github.com/nixos/nixpkgs"
 "#,
         );
 }
+
+#[test]
+fn sorting_with_references() {
+    let values = vec!["foo", "qux", "bar"];
+    let mut array = toml_edit::Array::from_iter(values);
+    array.sort_by(|lhs, rhs| lhs.as_str().cmp(&rhs.as_str()));
+}