refactor Value::follow_cell_path to reduce clones and return Cow (#15640)

# Description
While working on something else, I noticed that
`Value::follow_cell_path` receives `self`.

While it would be ideal for the signature to be `(&'a self, cell_path)
-> &'a Value`, that's not possible because:
1. Selecting a row from a list and field from a record can be done with
a reference but selecting a column from a table requires creating a new
list.
2. `Value::Custom` returns new `Value`s when indexed.

So the signature becomes `(&'a self, cell_path) -> Cow<'a, Value>`.

Another complication that arises is, once a new `Value` is created, and
it is further indexed, the `current` variable
1. can't be `&'a Value`, as the lifetime requirement means it can't
refer to local variables
2. _shouldn't_ be `Cow<'a, Value>`, as once it becomes an owned value,
it can't be borrowed ever again, as `current` is derived from its
previous value in further iterations. So once it's owned, it can't be
indexed by reference, leading to more clones

We need `current` to have _two_ possible lifetimes
1. `'out`: references derived from `&self`
2. `'local`: references derived from an owned value stored in a local
variable

```rust
enum MultiLife<'out, 'local, T>
where
    'out: 'local,
    T: ?Sized,
{
    Out(&'out T),
    Local(&'local T),
}
```
With `current: MultiLife<'out, '_, Value>`, we can traverse values with
minimal clones, and we can transform it to `Cow<'out, Value>` easily
(`MultiLife::Out -> Cow::Borrowed, MultiLife::Local -> Cow::Owned`) to
return it

# User-Facing Changes

# Tests + Formatting

# After Submitting

---------

Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
This commit is contained in:
Bahex
2025-05-01 17:43:57 +03:00
committed by GitHub
parent deca337a56
commit 55de232a1c
19 changed files with 373 additions and 336 deletions

View File

@ -1,3 +1,5 @@
use std::borrow::Cow;
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
use nu_engine::{column::get_columns, eval_variable};
use nu_protocol::{
@ -101,7 +103,9 @@ pub(crate) fn eval_cell_path(
} else {
eval_constant(working_set, head)
}?;
head_value.follow_cell_path(path_members, false)
head_value
.follow_cell_path(path_members, false)
.map(Cow::into_owned)
}
fn get_suggestions_by_value(