How to wrap &str

I am developing a linter (for an internal DSL that probably should have been thrown away decades ago). A core data type we are currently using is what we call a "ScriptString". This is essentially just:

struct ScriptString {
    text: String,
    source_document: Uri,
    source_range: Range,
    // Additional fields to keep track of things for error reporting and analysis
}

The issue that we are running into is that we are finding that we need to implement a bunch of the standard string methods from Str for this. Bassically, any str method that returns an &str wants to become a ScriptString method that returns a new ScriptString. Text, source_document, and source_range get populated in the obvious way; and the additional fields get set to default values.

This approach is pretty wasteful since we need to copy substrings instead of just taking a slice, but we don't really care about that.

The problem that is causing us grief is that we cannot figure out a good way to use most of the built-in str methods, so we have been manually reimplementing them with ugly and error prone index arithmethic.

The closest thing to a solution we have found would be to use substr_range, but that API is still not stable.

Is there a different solution to this problem we can try?

the method call operator resolve the method name through deref coersion, so you can implement the Deref trait:

impl Deref for ScriptString {
    type Target = str;
    fn deref(&mut self) -> &str {
        &self.text[self.source_range]
    }
}

DerefMut is optional, since most commonly used &str meethods don't require &mut, but you can implement DerefMut if you need these methods, such as as_mut_ptr(), get_mut().

You can implement your own substr_range based on as_bytes and element_offset (I recommend using a unique name).

Wouldn't it be feasible to create a method apply_str that takes a fn: &str -> &str for your ScriptString?

The idiomatic one that comes to mind is to implement an .as_str() method (or the AsStr trait) and chain on that

So the obvious way for source_range is adjust according to the returned slice? Then there is no other way around, to write this all from hand.

Maybe you can use some macro kung fu for repetitive code, but these days, assemble a list for the methods you require, add some instructions, and give it to an AI tool of your choice. The result is finished in a few minutes, including tests and docs.

If you do this, prepare some helper methods by hand, maybe something like this:

impl ScriptString {
    fn copy_offset_slice(&self, offset: usize, s: &str) -> Self {
        // calculate `source_range` from offset and `s`
        ...
    }
}

The trick is, prepare some universal tools for the AI to use and instruct the AI to use these tools. So there are no repetitive code and obscure bugs.

I don't believe it's actually the solution to their question, but usually I've seen/used AsRef<str>, with the benefit that it's a trait in the standard library.