Add Value::coerce_str (#11885)

# Description
Following #11851, this PR adds one final conversion function for
`Value`. `Value::coerce_str` takes a `&Value` and converts it to a
`Cow<str>`, creating an owned `String` for types that needed converting.
Otherwise, it returns a borrowed `str` for `String` and `Binary`
`Value`s which avoids a clone/allocation. Where possible, `coerce_str`
and `coerce_into_string` should be used instead of `coerce_string`,
since `coerce_string` always allocates a new `String`.
This commit is contained in:
Ian Manske
2024-02-18 16:47:10 +00:00
committed by GitHub
parent fb4251aba7
commit 68fcd71898
34 changed files with 115 additions and 83 deletions

View File

@ -15,7 +15,7 @@ pub(super) fn process_string_enum<T, E>(
E: Display,
{
let span = value.span();
if let Ok(v) = value.coerce_string() {
if let Ok(v) = value.coerce_str() {
match v.parse() {
Ok(format) => {
*config_point = format;

View File

@ -539,7 +539,7 @@ impl Value {
process_bool_config(value, &mut errors, &mut config.filesize_metric);
}
"format" => {
if let Ok(v) = value.coerce_string() {
if let Ok(v) = value.coerce_str() {
config.filesize_format = v.to_lowercase();
} else {
report_invalid_value("should be a string", span, &mut errors);

View File

@ -272,7 +272,7 @@ pub(super) fn try_parse_trim_strategy(
}
fn try_parse_trim_methodology(value: &Value) -> Option<TrimStrategy> {
if let Ok(value) = value.coerce_string() {
if let Ok(value) = value.coerce_str() {
match value.to_lowercase().as_str() {
"wrapping" => {
return Some(TrimStrategy::Wrap {

View File

@ -356,7 +356,7 @@ impl Value {
}
}
/// Returns this `Value` converted to a `String` or an error if it cannot be converted
/// Returns this `Value` converted to a `str` or an error if it cannot be converted
///
/// Only the following `Value` cases will return an `Ok` result:
/// - `Int`
@ -365,9 +365,56 @@ impl Value {
/// - `Binary` (only if valid utf-8)
/// - `Date`
///
/// Prefer [`coerce_into_string`](Self::coerce_into_string)
/// ```
/// # use nu_protocol::Value;
/// for val in Value::test_values() {
/// assert_eq!(
/// matches!(
/// val,
/// Value::Int { .. }
/// | Value::Float { .. }
/// | Value::String { .. }
/// | Value::Binary { .. }
/// | Value::Date { .. }
/// ),
/// val.coerce_str().is_ok(),
/// );
/// }
/// ```
pub fn coerce_str(&self) -> Result<Cow<str>, ShellError> {
match self {
Value::Int { val, .. } => Ok(Cow::Owned(val.to_string())),
Value::Float { val, .. } => Ok(Cow::Owned(val.to_string())),
Value::String { val, .. } => Ok(Cow::Borrowed(val)),
Value::Binary { val, .. } => match std::str::from_utf8(val) {
Ok(s) => Ok(Cow::Borrowed(s)),
Err(_) => self.cant_convert_to("string"),
},
Value::Date { val, .. } => Ok(Cow::Owned(
val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
)),
val => val.cant_convert_to("string"),
}
}
/// Returns this `Value` converted to a `String` or an error if it cannot be converted
///
/// # Note
/// This function is equivalent to `value.coerce_str().map(Cow::into_owned)`
/// which might allocate a new `String`.
///
/// To avoid this allocation, prefer [`coerce_str`](Self::coerce_str)
/// if you do not need an owned `String`,
/// or [`coerce_into_string`](Self::coerce_into_string)
/// if you do not need to keep the original `Value` around.
///
/// Only the following `Value` cases will return an `Ok` result:
/// - `Int`
/// - `Float`
/// - `String`
/// - `Binary` (only if valid utf-8)
/// - `Date`
///
/// ```
/// # use nu_protocol::Value;
/// for val in Value::test_values() {
@ -385,17 +432,7 @@ impl Value {
/// }
/// ```
pub fn coerce_string(&self) -> Result<String, ShellError> {
match self {
Value::Int { val, .. } => Ok(val.to_string()),
Value::Float { val, .. } => Ok(val.to_string()),
Value::String { val, .. } => Ok(val.clone()),
Value::Binary { val, .. } => match std::str::from_utf8(val) {
Ok(s) => Ok(s.to_string()),
Err(_) => self.cant_convert_to("string"),
},
Value::Date { val, .. } => Ok(val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)),
val => val.cant_convert_to("string"),
}
self.coerce_str().map(Cow::into_owned)
}
/// Returns this `Value` converted to a `String` or an error if it cannot be converted
@ -570,13 +607,13 @@ impl Value {
/// Returns this `Value` as a `u8` slice or an error if it cannot be converted
///
/// Prefer [`coerce_into_binary`](Self::coerce_into_binary)
/// if you do not need to keep the original `Value` around.
///
/// Only the following `Value` cases will return an `Ok` result:
/// - `Binary`
/// - `String`
///
/// Prefer [`coerce_into_binary`](Self::coerce_into_binary)
/// if you do not need to keep the original `Value` around.
///
/// ```
/// # use nu_protocol::Value;
/// for val in Value::test_values() {