pubgrub-rs / pubgrub

PubGrub version solving algorithm implemented in Rust
https://pubgrub-rs.github.io/pubgrub/pubgrub/
Mozilla Public License 2.0
337 stars 29 forks source link

Allow changing the `Display` of `Range` #221

Closed konstin closed 1 month ago

konstin commented 1 month ago

In python, versions and version specifiers are formatted in particular way (PEP 440) that's different from pubgrub's default. We want to use the pubgrub::range::Range type for correctness and performance, but we'd like to override its Display impl. This is currently not possible with the newtype pattern due to segments of Range being private.

For reference, this is Display impl we want:

impl<V: Display + Eq> Display for Range<V> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        if self.segments.is_empty() {
            write!(f, "∅")?;
        } else {
            for (idx, segment) in self.segments.iter().enumerate() {
                if idx > 0 {
                    write!(f, " | ")?;
                }
                match segment {
                    (Unbounded, Unbounded) => write!(f, "*")?,
                    (Unbounded, Included(v)) => write!(f, "<={v}")?,
                    (Unbounded, Excluded(v)) => write!(f, "<{v}")?,
                    (Included(v), Unbounded) => write!(f, ">={v}")?,
                    (Included(v), Included(b)) => {
                        if v == b {
                            write!(f, "=={v}")?
                        } else {
                            write!(f, ">={v}, <={b}")?
                        }
                    }
                    (Included(v), Excluded(b)) => write!(f, ">={v}, <{b}")?,
                    (Excluded(v), Unbounded) => write!(f, ">{v}")?,
                    (Excluded(v), Included(b)) => write!(f, ">{v}, <={b}")?,
                    (Excluded(v), Excluded(b)) => write!(f, ">{v}, <{b}")?,
                };
            }
        }
        Ok(())
    }
}
zanieb commented 1 month ago

Can we just add it to the report formatter?

mpizenberg commented 1 month ago

We didn’t make the internal representation of range public because it is easy to break guaranties if you have access to it. If I’m not mistaken though there is an iterator that gives lets you iterate over segments bounds. Could you use that to make a custom function to convert to a string?

Eh2406 commented 1 month ago

Isn't this exactly what #187 was intended to be used for? In your sample code self.segments.iter() can be replaced with self.iter() and it should all work.

konstin commented 1 month ago

Sorry, i missed the iter() for not returning Interval bit (Bound, Bound) :facepalm:. I've put up a half-baked branch at https://github.com/astral-sh/uv/compare/main...konsti/pubgrub-range, the main task and verbosity is to forward all the utility methods of Range (Maybe something for a trait? Feels a bit specific though).