uv/crates/pep508-rs/src/unnamed.rs
Andrew Gallant 1089abda3f
require serde and rkyv everywhere; remove optional serde and rkyv features (#3345)
In *some* places in our crates, `serde` (and `rkyv`) are optional
dependencies. I believe this was done out of reasons of "good sense,"
that is, it follows a Rust ecosystem pattern where serde integration
tends to be an opt-in crate feature. (And similarly for `rkyv`.)

However, ultimately, `uv` itself requires `serde` and `rkyv` to
function. Since our crates are strictly internal, there are limited
consumers for our crates without `serde` (and `rkyv`) enabled. I think
one possibility is that optional `serde` (and `rkyv`) integration means
that someone can do this:

    cargo test -p pep440_rs

And this will run tests _without_ `serde` or `rkyv` enabled. That in
turn could lead to faster iteration time by reducing compile times. But,
I'm not sure this is worth supporting. The iterative compilation times
of
individual crates are probably fast enough in debug mode, even with
`serde` and `rkyv` enabled. Namely, `serde` and `rkyv` themselves
shouldn't need to be re-compiled in most cases. On `main`:

```
from-scratch: `cargo test -p pep440_rs --lib` 0.685
incremental: `cargo test -p pep440_rs --lib` 0.278s
from-scratch: `cargo test -p pep440_rs --features serde,rkyv --lib` 3.948s
incremental: `cargo test -p pep440_rs --features serde,rkyv --lib` 0.321s
```

So while a from-scratch build does take significantly longer, an
incremental build is about the same.

The benefit of doing this change is two-fold:

1. It brings out crates into alignment with "reality." In particular,
   some crates were _implicitly_ relying on `serde` being enabled
   without explicitly declaring it. This technically means that our
   `Cargo.toml`s were wrong in some cases, but it is hard to observe it
   because of feature unification in a Cargo workspace.
2. We no longer need to deal with the cognitive burden of writing
   `#[cfg_attr(feature = "serde", ...)]` everywhere.
2024-05-03 10:21:03 -04:00

99 lines
3.2 KiB
Rust

use std::fmt::{Display, Formatter};
use std::path::Path;
use std::str::FromStr;
#[cfg(feature = "pyo3")]
use pyo3::pyclass;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use uv_normalize::ExtraName;
use crate::{Cursor, MarkerEnvironment, MarkerTree, Pep508Error, VerbatimUrl};
/// A PEP 508-like, direct URL dependency specifier without a package name.
///
/// In a `requirements.txt` file, the name of the package is optional for direct URL
/// dependencies. This isn't compliant with PEP 508, but is common in `requirements.txt`, which
/// is implementation-defined.
#[derive(Hash, Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "pyo3", pyclass(module = "pep508"))]
pub struct UnnamedRequirement {
/// The direct URL that defines the version specifier.
pub url: VerbatimUrl,
/// The list of extras such as `security`, `tests` in
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`.
pub extras: Vec<ExtraName>,
/// The markers such as `python_version > "3.8"` in
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`.
/// Those are a nested and/or tree.
pub marker: Option<MarkerTree>,
}
impl UnnamedRequirement {
/// Returns whether the markers apply for the given environment
pub fn evaluate_markers(&self, env: &MarkerEnvironment, extras: &[ExtraName]) -> bool {
if let Some(marker) = &self.marker {
marker.evaluate(env, extras)
} else {
true
}
}
}
impl Display for UnnamedRequirement {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.url)?;
if !self.extras.is_empty() {
write!(
f,
"[{}]",
self.extras
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(",")
)?;
}
if let Some(marker) = &self.marker {
write!(f, " ; {}", marker)?;
}
Ok(())
}
}
/// <https://github.com/serde-rs/serde/issues/908#issuecomment-298027413>
impl<'de> Deserialize<'de> for UnnamedRequirement {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
}
}
/// <https://github.com/serde-rs/serde/issues/1316#issue-332908452>
impl Serialize for UnnamedRequirement {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(self)
}
}
impl FromStr for UnnamedRequirement {
type Err = Pep508Error;
/// Parse a PEP 508-like direct URL requirement without a package name.
fn from_str(input: &str) -> Result<Self, Self::Err> {
crate::parse_unnamed_requirement(&mut Cursor::new(input), None)
}
}
impl UnnamedRequirement {
/// Parse a PEP 508-like direct URL requirement without a package name.
pub fn parse(input: &str, working_dir: impl AsRef<Path>) -> Result<Self, Pep508Error> {
crate::parse_unnamed_requirement(&mut Cursor::new(input), Some(working_dir.as_ref()))
}
}