ariadne icon indicating copy to clipboard operation
ariadne copied to clipboard

FileCache broken due to Path ?Sized requirementes for Span, and due to ?Sized requirements on tuples.

Open Aurora2500 opened this issue 8 months ago • 3 comments
trafficstars

FileCache implements Cache<Path>, but Path being !Sized leading to some issues.

Types like Report are generic over a Span, which is implemented for (Id, Range<usize>) for a few relevant types, but when used with FileCache this has the issue that the implementation for Span for (Id, Range<usize>) doesn't allow for an unsized Id. Aditionally, trying to just add that constraint won't work, as it leaves an unsized element be non-last in a tuple, which won't work as per rust rules.

Aurora2500 avatar Mar 16 '25 18:03 Aurora2500

Yep, this is an annoying gap in the API. In ariadne2 I've made an attempt at improving this.

If you have a suggested solution that leaves the existing API broadly intact (to minimise the changes that dependent code needs to make), I'm all ears!

zesterer avatar Mar 17 '25 16:03 zesterer

First idea that popped in my mind, for files & Paths specifically, would be to make FileCache generic over some additional generic type T: AsRef<Path>, such that it stores those internally & passes it around, but then when it needs to do the actual accessing the FS for reading files it calls id.as_ref().

With this, users could use either String, Box<Path>, Cow<Path>, Arc<Path>, or any similar type as they please to work as the id for file caches.

Aurora2500 avatar Mar 17 '25 21:03 Aurora2500

For anyone who wonders how to use FileCache with PathBuf. You can create your own implementation of a Span and work around it that way:

#[derive(Debug, Clone)]
pub struct ErrorSpan {
    file_path: PathBuf,
    span: MySpan,
}

impl ariadne::Span for ErrorSpan {
    type SourceId = std::path::Path;

    fn source(&self) -> &Self::SourceId {
        &self.file_path.as_path()
    }

    fn start(&self) -> usize {
        self.span.start()
    }

    fn end(&self) -> usize {
        self.span.end()
    }
}

then you can build a report with a signature like this:

pub fn build<'a>(diagnostic: &Diagnostic) -> Report<'a, ErrorSpan> {
   ...
}

and finally create a string like this:

pub fn render(diagnostic: &Diagnostic) -> String {
    let report = build(diagnostic);
    let mut buf = Vec::new();
    let cache = ariadne::FileCache::default();
    report.write(cache, &mut buf).unwrap();
    String::from_utf8_lossy(&buf).to_string()
}

honungsburk avatar Jun 08 '25 07:06 honungsburk