Implement IntoValue for more types (#13744)

# Description

Implements `IntoValue` for `&str` and `DateTime` as well as other
nushell types like `Record` and `Closure`. Also allows `HashMap`s with
keys besides `String` to implement `IntoValue`.
This commit is contained in:
Ian Manske 2024-09-01 10:02:12 -07:00 committed by GitHub
parent f4940e115f
commit e3f59910b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 65 additions and 18 deletions

View File

@ -377,7 +377,7 @@ fn file_completions_with_mixed_separators() {
file(dir.join("lib-dir1").join("baz.nu")),
file(dir.join("lib-dir1").join("xyzzy.nu")),
];
let expecetd_slash_paths: Vec<String> = expected_paths
let expected_slash_paths: Vec<String> = expected_paths
.iter()
.map(|s| s.replace(MAIN_SEPARATOR, "/"))
.collect();
@ -385,22 +385,22 @@ fn file_completions_with_mixed_separators() {
let target_dir = format!("ls {dir_str}/lib-dir1/");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expecetd_slash_paths, &suggestions);
match_suggestions(&expected_slash_paths, &suggestions);
let target_dir = format!("cp {dir_str}\\lib-dir1/");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expecetd_slash_paths, &suggestions);
match_suggestions(&expected_slash_paths, &suggestions);
let target_dir = format!("ls {dir_str}/lib-dir1\\/");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expecetd_slash_paths, &suggestions);
match_suggestions(&expected_slash_paths, &suggestions);
let target_dir = format!("ls {dir_str}\\lib-dir1\\/");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expecetd_slash_paths, &suggestions);
match_suggestions(&expected_slash_paths, &suggestions);
let target_dir = format!("ls {dir_str}\\lib-dir1\\");
let suggestions = completer.complete(&target_dir, target_dir.len());

View File

@ -191,7 +191,7 @@ fn enum_into_value(
.as_str()
.to_case(container_attrs.rename_all.unwrap_or(Case::Snake));
match &variant.fields {
// In the future we can implement more complexe enums here.
// In the future we can implement more complex enums here.
Fields::Named(fields) => Err(DeriveError::UnsupportedEnums {
fields_span: fields.span(),
}),

View File

@ -1,6 +1,6 @@
use std::collections::HashMap;
use crate::{Record, ShellError, Span, Value};
use crate::{ast::CellPath, engine::Closure, Range, Record, ShellError, Span, Value};
use chrono::{DateTime, FixedOffset};
use std::{borrow::Borrow, collections::HashMap};
/// A trait for converting a value into a [`Value`].
///
@ -152,6 +152,12 @@ impl IntoValue for String {
}
}
impl IntoValue for &str {
fn into_value(self, span: Span) -> Value {
Value::string(self, span)
}
}
impl<T> IntoValue for Vec<T>
where
T: IntoValue,
@ -173,24 +179,59 @@ where
}
}
impl<V> IntoValue for HashMap<String, V>
impl<K, V> IntoValue for HashMap<K, V>
where
K: Borrow<str> + Into<String>,
V: IntoValue,
{
fn into_value(self, span: Span) -> Value {
let mut record = Record::new();
for (k, v) in self.into_iter() {
// Using `push` is fine as a hashmaps have unique keys.
// To ensure this uniqueness, we only allow hashmaps with strings as
// keys and not keys which implement `Into<String>` or `ToString`.
record.push(k, v.into_value(span));
}
Value::record(record, span)
// The `Borrow<str>` constraint is to ensure uniqueness, as implementations of `Borrow`
// must uphold by certain properties (e.g., `(x == y) == (x.borrow() == y.borrow())`.
//
// The `Into<String>` constraint is necessary for us to convert the key into a `String`.
// Most types that implement `Borrow<str>` also implement `Into<String>`.
// Implementations of `Into` must also be lossless and value-preserving conversions.
// So, when combined with the `Borrow` constraint, this means that the converted
// `String` keys should be unique.
self.into_iter()
.map(|(k, v)| (k.into(), v.into_value(span)))
.collect::<Record>()
.into_value(span)
}
}
// Nu Types
impl IntoValue for Range {
fn into_value(self, span: Span) -> Value {
Value::range(self, span)
}
}
impl IntoValue for Record {
fn into_value(self, span: Span) -> Value {
Value::record(self, span)
}
}
impl IntoValue for Closure {
fn into_value(self, span: Span) -> Value {
Value::closure(self, span)
}
}
impl IntoValue for ShellError {
fn into_value(self, span: Span) -> Value {
Value::error(self, span)
}
}
impl IntoValue for CellPath {
fn into_value(self, span: Span) -> Value {
Value::cell_path(self, span)
}
}
impl IntoValue for Value {
fn into_value(self, span: Span) -> Value {
self.with_span(span)
@ -199,6 +240,12 @@ impl IntoValue for Value {
// Foreign Types
impl IntoValue for DateTime<FixedOffset> {
fn into_value(self, span: Span) -> Value {
Value::date(self, span)
}
}
impl IntoValue for bytes::Bytes {
fn into_value(self, span: Span) -> Value {
Value::binary(self.to_vec(), span)