Migrate lock errors to thiserror (#4225)

## Summary

Do we prefer this?
This commit is contained in:
Charlie Marsh 2024-06-11 04:40:48 -07:00 committed by GitHub
parent 656fc427b9
commit 33cf47182f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -70,7 +70,10 @@ impl Lock {
} }
let id = locked_dist.id.clone(); let id = locked_dist.id.clone();
if let Some(locked_dist) = locked_dists.insert(id, locked_dist) { if let Some(locked_dist) = locked_dists.insert(id, locked_dist) {
return Err(LockError::duplicate_distribution(locked_dist.id)); return Err(LockErrorKind::DuplicateDistribution {
id: locked_dist.id.clone(),
}
.into());
} }
} }
} }
@ -81,7 +84,11 @@ impl Lock {
if let Some(extra) = dist.extra.as_ref() { if let Some(extra) = dist.extra.as_ref() {
let id = DistributionId::from_annotated_dist(dist); let id = DistributionId::from_annotated_dist(dist);
let Some(locked_dist) = locked_dists.get_mut(&id) else { let Some(locked_dist) = locked_dists.get_mut(&id) else {
return Err(LockError::missing_extra_base(id, extra.clone())); return Err(LockErrorKind::MissingExtraBase {
id,
extra: extra.clone(),
}
.into());
}; };
for edge in graph.petgraph.edges(node_index) { for edge in graph.petgraph.edges(node_index) {
let dependency_dist = &graph.petgraph[edge.target()]; let dependency_dist = &graph.petgraph[edge.target()];
@ -92,7 +99,11 @@ impl Lock {
if let Some(group) = dist.dev.as_ref() { if let Some(group) = dist.dev.as_ref() {
let id = DistributionId::from_annotated_dist(dist); let id = DistributionId::from_annotated_dist(dist);
let Some(locked_dist) = locked_dists.get_mut(&id) else { let Some(locked_dist) = locked_dists.get_mut(&id) else {
return Err(LockError::missing_dev_base(id, group.clone())); return Err(LockErrorKind::MissingDevBase {
id,
group: group.clone(),
}
.into());
}; };
for edge in graph.petgraph.edges(node_index) { for edge in graph.petgraph.edges(node_index) {
let dependency_dist = &graph.petgraph[edge.target()]; let dependency_dist = &graph.petgraph[edge.target()];
@ -341,10 +352,11 @@ impl TryFrom<LockWire> for Lock {
for windows in dist.dependencies.windows(2) { for windows in dist.dependencies.windows(2) {
let (dep1, dep2) = (&windows[0], &windows[1]); let (dep1, dep2) = (&windows[0], &windows[1]);
if dep1 == dep2 { if dep1 == dep2 {
return Err(LockError::duplicate_dependency( return Err(LockErrorKind::DuplicateDependency {
dist.id.clone(), id: dist.id.clone(),
dep1.clone(), dependency: dep1.clone(),
)); }
.into());
} }
} }
@ -354,10 +366,12 @@ impl TryFrom<LockWire> for Lock {
for windows in dependencies.windows(2) { for windows in dependencies.windows(2) {
let (dep1, dep2) = (&windows[0], &windows[1]); let (dep1, dep2) = (&windows[0], &windows[1]);
if dep1 == dep2 { if dep1 == dep2 {
return Err(LockError::duplicate_dependency( return Err(LockErrorKind::DuplicateOptionalDependency {
dist.id.clone(), id: dist.id.clone(),
dep1.clone(), extra: extra.clone(),
)); dependency: dep1.clone(),
}
.into());
} }
} }
} }
@ -368,10 +382,12 @@ impl TryFrom<LockWire> for Lock {
for windows in dependencies.windows(2) { for windows in dependencies.windows(2) {
let (dep1, dep2) = (&windows[0], &windows[1]); let (dep1, dep2) = (&windows[0], &windows[1]);
if dep1 == dep2 { if dep1 == dep2 {
return Err(LockError::duplicate_dependency( return Err(LockErrorKind::DuplicateDevDependency {
dist.id.clone(), id: dist.id.clone(),
dep1.clone(), group: group.clone(),
)); dependency: dep1.clone(),
}
.into());
} }
} }
} }
@ -384,7 +400,10 @@ impl TryFrom<LockWire> for Lock {
let mut by_id = FxHashMap::default(); let mut by_id = FxHashMap::default();
for (i, dist) in wire.distributions.iter().enumerate() { for (i, dist) in wire.distributions.iter().enumerate() {
if by_id.insert(dist.id.clone(), i).is_some() { if by_id.insert(dist.id.clone(), i).is_some() {
return Err(LockError::duplicate_distribution(dist.id.clone())); return Err(LockErrorKind::DuplicateDistribution {
id: dist.id.clone(),
}
.into());
} }
} }
@ -397,18 +416,20 @@ impl TryFrom<LockWire> for Lock {
let dep_dist = &wire.distributions[*index]; let dep_dist = &wire.distributions[*index];
if let Some(extra) = &dep.extra { if let Some(extra) = &dep.extra {
if !dep_dist.optional_dependencies.contains_key(extra) { if !dep_dist.optional_dependencies.contains_key(extra) {
return Err(LockError::unrecognized_extra( return Err(LockErrorKind::UnrecognizedExtra {
dist.id.clone(), id: dist.id.clone(),
dep.clone(), dependency: dep.clone(),
extra.clone(), extra: extra.clone(),
)); }
.into());
} }
} }
} else { } else {
return Err(LockError::unrecognized_dependency( return Err(LockErrorKind::UnrecognizedDependency {
dist.id.clone(), id: dist.id.clone(),
dep.clone(), dependency: dep.clone(),
)); }
.into());
} }
} }
@ -419,18 +440,20 @@ impl TryFrom<LockWire> for Lock {
let dep_dist = &wire.distributions[*index]; let dep_dist = &wire.distributions[*index];
if let Some(extra) = &dep.extra { if let Some(extra) = &dep.extra {
if !dep_dist.optional_dependencies.contains_key(extra) { if !dep_dist.optional_dependencies.contains_key(extra) {
return Err(LockError::unrecognized_extra( return Err(LockErrorKind::UnrecognizedExtra {
dist.id.clone(), id: dist.id.clone(),
dep.clone(), dependency: dep.clone(),
extra.clone(), extra: extra.clone(),
)); }
.into());
} }
} }
} else { } else {
return Err(LockError::unrecognized_dependency( return Err(LockErrorKind::UnrecognizedDependency {
dist.id.clone(), id: dist.id.clone(),
dep.clone(), dependency: dep.clone(),
)); }
.into());
} }
} }
} }
@ -442,18 +465,20 @@ impl TryFrom<LockWire> for Lock {
let dep_dist = &wire.distributions[*index]; let dep_dist = &wire.distributions[*index];
if let Some(extra) = &dep.extra { if let Some(extra) = &dep.extra {
if !dep_dist.optional_dependencies.contains_key(extra) { if !dep_dist.optional_dependencies.contains_key(extra) {
return Err(LockError::unrecognized_extra( return Err(LockErrorKind::UnrecognizedExtra {
dist.id.clone(), id: dist.id.clone(),
dep.clone(), dependency: dep.clone(),
extra.clone(), extra: extra.clone(),
)); }
.into());
} }
} }
} else { } else {
return Err(LockError::unrecognized_dependency( return Err(LockErrorKind::UnrecognizedDependency {
dist.id.clone(), id: dist.id.clone(),
dep.clone(), dependency: dep.clone(),
)); }
.into());
} }
} }
} }
@ -463,16 +488,22 @@ impl TryFrom<LockWire> for Lock {
let requires_hash = dist.id.source.kind.requires_hash(); let requires_hash = dist.id.source.kind.requires_hash();
if let Some(ref sdist) = dist.sdist { if let Some(ref sdist) = dist.sdist {
if requires_hash != sdist.hash.is_some() { if requires_hash != sdist.hash.is_some() {
return Err(LockError::hash( return Err(LockErrorKind::Hash {
dist.id.clone(), id: dist.id.clone(),
"source distribution", artifact_type: "source distribution",
requires_hash, expected: requires_hash,
)); }
.into());
} }
} }
for wheel in &dist.wheels { for wheel in &dist.wheels {
if requires_hash != wheel.hash.is_some() { if requires_hash != wheel.hash.is_some() {
return Err(LockError::hash(dist.id.clone(), "wheel", requires_hash)); return Err(LockErrorKind::Hash {
id: dist.id.clone(),
artifact_type: "wheel",
expected: requires_hash,
}
.into());
} }
} }
} }
@ -590,14 +621,21 @@ impl Distribution {
let built_dist = BuiltDist::DirectUrl(direct_dist); let built_dist = BuiltDist::DirectUrl(direct_dist);
Ok(Dist::Built(built_dist)) Ok(Dist::Built(built_dist))
} }
SourceKind::Git(_) => Err(LockError::invalid_wheel_source(self.id.clone(), "Git")), SourceKind::Git(_) => Err(LockErrorKind::InvalidWheelSource {
SourceKind::Directory => Err(LockError::invalid_wheel_source( id: self.id.clone(),
self.id.clone(), source_type: "Git",
"directory",
)),
SourceKind::Editable => {
Err(LockError::invalid_wheel_source(self.id.clone(), "editable"))
} }
.into()),
SourceKind::Directory => Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(),
source_type: "directory",
}
.into()),
SourceKind::Editable => Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(),
source_type: "editable",
}
.into()),
}; };
} }
@ -694,7 +732,10 @@ impl Distribution {
}; };
} }
Err(LockError::neither_source_dist_nor_wheel(self.id.clone())) Err(LockErrorKind::NeitherSourceDistNorWheel {
id: self.id.clone(),
}
.into())
} }
fn find_best_wheel(&self, tags: &Tags) -> Option<usize> { fn find_best_wheel(&self, tags: &Tags) -> Option<usize> {
@ -902,10 +943,13 @@ impl std::str::FromStr for Source {
type Err = SourceParseError; type Err = SourceParseError;
fn from_str(s: &str) -> Result<Source, SourceParseError> { fn from_str(s: &str) -> Result<Source, SourceParseError> {
let (kind, url) = s let (kind, url) = s.split_once('+').ok_or_else(|| SourceParseError::NoPlus {
.split_once('+') given: s.to_string(),
.ok_or_else(|| SourceParseError::no_plus(s))?; })?;
let mut url = Url::parse(url).map_err(|err| SourceParseError::invalid_url(s, err))?; let mut url = Url::parse(url).map_err(|err| SourceParseError::InvalidUrl {
given: s.to_string(),
err,
})?;
match kind { match kind {
"registry" => Ok(Source { "registry" => Ok(Source {
kind: SourceKind::Registry, kind: SourceKind::Registry,
@ -913,8 +957,12 @@ impl std::str::FromStr for Source {
}), }),
"git" => Ok(Source { "git" => Ok(Source {
kind: SourceKind::Git(GitSource::from_url(&mut url).map_err(|err| match err { kind: SourceKind::Git(GitSource::from_url(&mut url).map_err(|err| match err {
GitSourceError::InvalidSha => SourceParseError::invalid_sha(s), GitSourceError::InvalidSha => SourceParseError::InvalidSha {
GitSourceError::MissingSha => SourceParseError::missing_sha(s), given: s.to_string(),
},
GitSourceError::MissingSha => SourceParseError::MissingSha {
given: s.to_string(),
},
})?), })?),
url, url,
}), }),
@ -934,7 +982,10 @@ impl std::str::FromStr for Source {
kind: SourceKind::Editable, kind: SourceKind::Editable,
url, url,
}), }),
name => Err(SourceParseError::unrecognized_source_name(s, name)), name => Err(SourceParseError::UnrecognizedSourceName {
given: s.to_string(),
name: name.to_string(),
}),
} }
} }
} }
@ -1164,7 +1215,8 @@ impl SourceDist {
.file .file
.url .url
.to_url() .to_url()
.map_err(LockError::invalid_file_url)?; .map_err(LockErrorKind::InvalidFileUrl)
.map_err(LockError::from)?;
let hash = reg_dist.file.hashes.first().cloned().map(Hash::from); let hash = reg_dist.file.hashes.first().cloned().map(Hash::from);
let size = reg_dist.file.size; let size = reg_dist.file.size;
Ok(SourceDist { url, hash, size }) Ok(SourceDist { url, hash, size })
@ -1357,7 +1409,8 @@ impl Wheel {
.file .file
.url .url
.to_url() .to_url()
.map_err(LockError::invalid_file_url)?; .map_err(LockErrorKind::InvalidFileUrl)
.map_err(LockError::from)?;
let hash = wheel.file.hashes.first().cloned().map(Hash::from); let hash = wheel.file.hashes.first().cloned().map(Hash::from);
let size = wheel.file.size; let size = wheel.file.size;
Ok(Wheel { Ok(Wheel {
@ -1579,263 +1632,37 @@ impl<'de> serde::Deserialize<'de> for Hash {
} }
} }
#[derive(Clone, Debug, thiserror::Error)]
#[error(transparent)]
pub struct LockError(Box<LockErrorKind>);
impl<E> From<E> for LockError
where
LockErrorKind: From<E>,
{
fn from(err: E) -> Self {
LockError(Box::new(LockErrorKind::from(err)))
}
}
/// An error that occurs when generating a `Lock` data structure. /// An error that occurs when generating a `Lock` data structure.
/// ///
/// These errors are sometimes the result of possible programming bugs. /// These errors are sometimes the result of possible programming bugs.
/// For example, if there are two or more duplicative distributions given /// For example, if there are two or more duplicative distributions given
/// to `Lock::new`, then an error is returned. It's likely that the fault /// to `Lock::new`, then an error is returned. It's likely that the fault
/// is with the caller somewhere in such cases. /// is with the caller somewhere in such cases.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, thiserror::Error)]
pub struct LockError {
kind: Box<LockErrorKind>,
}
impl LockError {
fn neither_source_dist_nor_wheel(id: DistributionId) -> LockError {
let kind = LockErrorKind::NeitherSourceDistNorWheel { id };
LockError {
kind: Box::new(kind),
}
}
}
impl LockError {
fn duplicate_distribution(id: DistributionId) -> LockError {
let kind = LockErrorKind::DuplicateDistribution { id };
LockError {
kind: Box::new(kind),
}
}
fn duplicate_dependency(id: DistributionId, dependency: Dependency) -> LockError {
let kind = LockErrorKind::DuplicateDependency { id, dependency };
LockError {
kind: Box::new(kind),
}
}
fn duplicate_optional_dependency(
id: DistributionId,
extra: ExtraName,
dependency: Dependency,
) -> LockError {
let kind = LockErrorKind::DuplicateOptionalDependency {
id,
extra,
dependency,
};
LockError {
kind: Box::new(kind),
}
}
fn duplicate_dev_dependency(
id: DistributionId,
group: GroupName,
dependency: Dependency,
) -> LockError {
let kind = LockErrorKind::DuplicateDevDependency {
id,
group,
dependency,
};
LockError {
kind: Box::new(kind),
}
}
fn invalid_file_url(err: ToUrlError) -> LockError {
let kind = LockErrorKind::InvalidFileUrl { err };
LockError {
kind: Box::new(kind),
}
}
fn unrecognized_dependency(id: DistributionId, dependency: Dependency) -> LockError {
let err = UnrecognizedDependencyError { id, dependency };
let kind = LockErrorKind::UnrecognizedDependency { err };
LockError {
kind: Box::new(kind),
}
}
fn unrecognized_extra(
id: DistributionId,
dependency: Dependency,
extra: ExtraName,
) -> LockError {
let kind = LockErrorKind::UnrecognizedExtra {
id,
dependency,
extra,
};
LockError {
kind: Box::new(kind),
}
}
fn hash(id: DistributionId, artifact_type: &'static str, expected: bool) -> LockError {
let kind = LockErrorKind::Hash {
id,
artifact_type,
expected,
};
LockError {
kind: Box::new(kind),
}
}
fn missing_extra_base(id: DistributionId, extra: ExtraName) -> LockError {
let kind = LockErrorKind::MissingExtraBase { id, extra };
LockError {
kind: Box::new(kind),
}
}
fn missing_dev_base(id: DistributionId, group: GroupName) -> LockError {
let kind = LockErrorKind::MissingDevBase { id, group };
LockError {
kind: Box::new(kind),
}
}
fn invalid_wheel_source(id: DistributionId, source: &'static str) -> LockError {
let kind = LockErrorKind::InvalidWheelSource { id, source };
LockError {
kind: Box::new(kind),
}
}
}
impl std::error::Error for LockError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self.kind {
LockErrorKind::DuplicateDistribution { .. } => None,
LockErrorKind::DuplicateDependency { .. } => None,
LockErrorKind::DuplicateOptionalDependency { .. } => None,
LockErrorKind::DuplicateDevDependency { .. } => None,
LockErrorKind::InvalidFileUrl { ref err } => Some(err),
LockErrorKind::UnrecognizedDependency { ref err } => Some(err),
LockErrorKind::UnrecognizedExtra { .. } => None,
LockErrorKind::Hash { .. } => None,
LockErrorKind::MissingExtraBase { .. } => None,
LockErrorKind::MissingDevBase { .. } => None,
LockErrorKind::InvalidWheelSource { .. } => None,
LockErrorKind::NeitherSourceDistNorWheel { .. } => None,
}
}
}
impl std::fmt::Display for LockError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self.kind {
LockErrorKind::DuplicateDistribution { ref id } => {
write!(f, "found duplicate distribution `{id}`")
}
LockErrorKind::DuplicateDependency {
ref id,
ref dependency,
} => {
write!(
f,
"for distribution `{id}`, found duplicate dependency `{dependency}`"
)
}
LockErrorKind::DuplicateOptionalDependency {
ref id,
ref extra,
ref dependency,
} => {
write!(
f,
"for distribution `{id}[{extra}]`, found duplicate dependency `{dependency}`"
)
}
LockErrorKind::DuplicateDevDependency {
ref id,
ref group,
ref dependency,
} => {
write!(
f,
"for distribution `{id}:{group}`, found duplicate dependency `{dependency}`"
)
}
LockErrorKind::InvalidFileUrl { .. } => {
write!(f, "failed to parse wheel or source dist URL")
}
LockErrorKind::UnrecognizedDependency { .. } => {
write!(f, "found unrecognized dependency")
}
LockErrorKind::UnrecognizedExtra {
ref id,
ref dependency,
ref extra,
} => {
write!(
f,
"for distribution `{id}`, found dependency `{dependency}` with unrecognized extra `{extra}`"
)
}
LockErrorKind::Hash {
ref id,
artifact_type,
expected: true,
} => {
write!(
f,
"since the distribution `{id}` comes from a {source} dependency, \
a hash was expected but one was not found for {artifact_type}",
source = id.source.kind.name(),
)
}
LockErrorKind::Hash {
ref id,
artifact_type,
expected: false,
} => {
write!(
f,
"since the distribution `{id}` comes from a {source} dependency, \
a hash was not expected but one was found for {artifact_type}",
source = id.source.kind.name(),
)
}
LockErrorKind::MissingExtraBase { ref id, ref extra } => {
write!(
f,
"found distribution `{id}` with extra `{extra}` but no base distribution",
)
}
LockErrorKind::MissingDevBase { ref id, ref group } => {
write!(
f,
"found distribution `{id}` with development dependency group `{group}` but no base distribution",
)
}
LockErrorKind::InvalidWheelSource { ref id, source } => {
write!(f, "wheels cannot come from {source} sources")
}
LockErrorKind::NeitherSourceDistNorWheel { ref id } => {
write!(
f,
"found distribution {id} with neither wheels nor source distribution"
)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum LockErrorKind { enum LockErrorKind {
/// An error that occurs when multiple distributions with the same /// An error that occurs when multiple distributions with the same
/// ID were found. /// ID were found.
#[error("found duplicate distribution `{id}`")]
DuplicateDistribution { DuplicateDistribution {
/// The ID of the conflicting distributions. /// The ID of the conflicting distributions.
id: DistributionId, id: DistributionId,
}, },
/// An error that occurs when there are multiple dependencies for the /// An error that occurs when there are multiple dependencies for the
/// same distribution that have identical identifiers. /// same distribution that have identical identifiers.
#[error("for distribution `{id}`, found duplicate dependency `{dependency}`")]
DuplicateDependency { DuplicateDependency {
/// The ID of the distribution for which a duplicate dependency was /// The ID of the distribution for which a duplicate dependency was
/// found. /// found.
@ -1846,6 +1673,7 @@ enum LockErrorKind {
/// An error that occurs when there are multiple dependencies for the /// An error that occurs when there are multiple dependencies for the
/// same distribution that have identical identifiers, as part of the /// same distribution that have identical identifiers, as part of the
/// that distribution's optional dependencies. /// that distribution's optional dependencies.
#[error("for distribution `{id}[{extra}]`, found duplicate dependency `{dependency}`")]
DuplicateOptionalDependency { DuplicateOptionalDependency {
/// The ID of the distribution for which a duplicate dependency was /// The ID of the distribution for which a duplicate dependency was
/// found. /// found.
@ -1858,6 +1686,7 @@ enum LockErrorKind {
/// An error that occurs when there are multiple dependencies for the /// An error that occurs when there are multiple dependencies for the
/// same distribution that have identical identifiers, as part of the /// same distribution that have identical identifiers, as part of the
/// that distribution's development dependencies. /// that distribution's development dependencies.
#[error("for distribution `{id}:{group}`, found duplicate dependency `{dependency}`")]
DuplicateDevDependency { DuplicateDevDependency {
/// The ID of the distribution for which a duplicate dependency was /// The ID of the distribution for which a duplicate dependency was
/// found. /// found.
@ -1869,20 +1698,29 @@ enum LockErrorKind {
}, },
/// An error that occurs when the URL to a file for a wheel or /// An error that occurs when the URL to a file for a wheel or
/// source dist could not be converted to a structured `url::Url`. /// source dist could not be converted to a structured `url::Url`.
InvalidFileUrl { #[error("failed to parse wheel or source distribution URL")]
InvalidFileUrl(
/// The underlying error that occurred. This includes the /// The underlying error that occurred. This includes the
/// errant URL in its error message. /// errant URL in its error message.
err: ToUrlError, #[source]
}, ToUrlError,
/// An error that occurs when the caller provides a distribution with a ),
/// dependency that doesn't correspond to any other distribution in the /// An error that occurs when there's an unrecognized dependency.
/// lock file. ///
/// That is, a dependency for a distribution that isn't in the lock file.
#[error(
"for distribution `{id}`, found dependency `{dependency}` with no locked distribution"
)]
UnrecognizedDependency { UnrecognizedDependency {
/// The actual error. /// The ID of the distribution that has an unrecognized dependency.
err: UnrecognizedDependencyError, id: DistributionId,
/// The ID of the dependency that doesn't have a corresponding distribution
/// entry.
dependency: Dependency,
}, },
/// An error that occurs when the caller provides a distribution with /// An error that occurs when the caller provides a distribution with
/// an extra that doesn't exist for the dependency. /// an extra that doesn't exist for the dependency.
#[error("for distribution `{id}`, found dependency `{dependency}` with unrecognized extra `{extra}`")]
UnrecognizedExtra { UnrecognizedExtra {
/// The ID of the distribution that has an unrecognized dependency. /// The ID of the distribution that has an unrecognized dependency.
id: DistributionId, id: DistributionId,
@ -1894,6 +1732,7 @@ enum LockErrorKind {
}, },
/// An error that occurs when a hash is expected (or not) for a particular /// An error that occurs when a hash is expected (or not) for a particular
/// artifact, but one was not found (or was). /// artifact, but one was not found (or was).
#[error("since the distribution `{id}` comes from a {source} dependency, a hash was {expected} but one was not found for {artifact_type}", source = id.source.kind.name(), expected = if *expected { "expected" } else { "not expected" })]
Hash { Hash {
/// The ID of the distribution that has a missing hash. /// The ID of the distribution that has a missing hash.
id: DistributionId, id: DistributionId,
@ -1905,6 +1744,7 @@ enum LockErrorKind {
}, },
/// An error that occurs when a distribution is included with an extra name, /// An error that occurs when a distribution is included with an extra name,
/// but no corresponding base distribution (i.e., without the extra) exists. /// but no corresponding base distribution (i.e., without the extra) exists.
#[error("found distribution `{id}` with extra `{extra}` but no base distribution")]
MissingExtraBase { MissingExtraBase {
/// The ID of the distribution that has a missing base. /// The ID of the distribution that has a missing base.
id: DistributionId, id: DistributionId,
@ -1914,6 +1754,7 @@ enum LockErrorKind {
/// An error that occurs when a distribution is included with a development /// An error that occurs when a distribution is included with a development
/// dependency group, but no corresponding base distribution (i.e., without /// dependency group, but no corresponding base distribution (i.e., without
/// the group) exists. /// the group) exists.
#[error("found distribution `{id}` with development dependency group `{group}` but no base distribution")]
MissingDevBase { MissingDevBase {
/// The ID of the distribution that has a missing base. /// The ID of the distribution that has a missing base.
id: DistributionId, id: DistributionId,
@ -1922,132 +1763,60 @@ enum LockErrorKind {
}, },
/// An error that occurs from an invalid lockfile where a wheel comes from a non-wheel source /// An error that occurs from an invalid lockfile where a wheel comes from a non-wheel source
/// such as a directory. /// such as a directory.
#[error("wheels cannot come from {source_type} sources")]
InvalidWheelSource { InvalidWheelSource {
/// The ID of the distribution that has a missing base. /// The ID of the distribution that has a missing base.
id: DistributionId, id: DistributionId,
/// The kind of the invalid source. /// The kind of the invalid source.
source: &'static str, source_type: &'static str,
}, },
/// An error that occurs when a distribution is included with neither wheels nor a source
/// distribution.
#[error("found distribution {id} with neither wheels nor source distribution")]
NeitherSourceDistNorWheel { NeitherSourceDistNorWheel {
/// The ID of the distribution that has a missing base. /// The ID of the distribution that has a missing base.
id: DistributionId, id: DistributionId,
}, },
} }
/// An error that occurs when there's an unrecognized dependency.
///
/// That is, a dependency for a distribution that isn't in the lock file.
#[derive(Clone, Debug, Eq, PartialEq)]
struct UnrecognizedDependencyError {
/// The ID of the distribution that has an unrecognized dependency.
id: DistributionId,
/// The ID of the dependency that doesn't have a corresponding distribution
/// entry.
dependency: Dependency,
}
impl std::error::Error for UnrecognizedDependencyError {}
impl std::fmt::Display for UnrecognizedDependencyError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let UnrecognizedDependencyError {
ref id,
ref dependency,
} = *self;
write!(
f,
"found dependency `{dependency}` for `{id}` with no locked distribution"
)
}
}
/// An error that occurs when a source string could not be parsed. /// An error that occurs when a source string could not be parsed.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, thiserror::Error)]
struct SourceParseError { enum SourceParseError {
given: String,
kind: SourceParseErrorKind,
}
impl SourceParseError {
fn no_plus(given: &str) -> SourceParseError {
let given = given.to_string();
let kind = SourceParseErrorKind::NoPlus;
SourceParseError { given, kind }
}
fn unrecognized_source_name(given: &str, name: &str) -> SourceParseError {
let given = given.to_string();
let kind = SourceParseErrorKind::UnrecognizedSourceName {
name: name.to_string(),
};
SourceParseError { given, kind }
}
fn invalid_url(given: &str, err: url::ParseError) -> SourceParseError {
let given = given.to_string();
let kind = SourceParseErrorKind::InvalidUrl { err };
SourceParseError { given, kind }
}
fn missing_sha(given: &str) -> SourceParseError {
let given = given.to_string();
let kind = SourceParseErrorKind::MissingSha;
SourceParseError { given, kind }
}
fn invalid_sha(given: &str) -> SourceParseError {
let given = given.to_string();
let kind = SourceParseErrorKind::InvalidSha;
SourceParseError { given, kind }
}
}
impl std::error::Error for SourceParseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self.kind {
SourceParseErrorKind::NoPlus
| SourceParseErrorKind::UnrecognizedSourceName { .. }
| SourceParseErrorKind::MissingSha
| SourceParseErrorKind::InvalidSha => None,
SourceParseErrorKind::InvalidUrl { ref err } => Some(err),
}
}
}
impl std::fmt::Display for SourceParseError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let given = &self.given;
match self.kind {
SourceParseErrorKind::NoPlus => write!(f, "could not find `+` in source `{given}`"),
SourceParseErrorKind::UnrecognizedSourceName { ref name } => {
write!(f, "unrecognized name `{name}` in source `{given}`")
}
SourceParseErrorKind::InvalidUrl { .. } => write!(f, "invalid URL in source `{given}`"),
SourceParseErrorKind::MissingSha => write!(f, "missing SHA in source `{given}`"),
SourceParseErrorKind::InvalidSha => write!(f, "invalid SHA in source `{given}`"),
}
}
}
/// The kind of error that can occur when parsing a source string.
#[derive(Clone, Debug, Eq, PartialEq)]
enum SourceParseErrorKind {
/// An error that occurs when no '+' could be found. /// An error that occurs when no '+' could be found.
NoPlus, #[error("could not find `+` in source `{given}`")]
NoPlus {
/// The source string given.
given: String,
},
/// An error that occurs when the source name was unrecognized. /// An error that occurs when the source name was unrecognized.
#[error("unrecognized name `{name}` in source `{given}`")]
UnrecognizedSourceName { UnrecognizedSourceName {
/// The source string given.
given: String,
/// The unrecognized name. /// The unrecognized name.
name: String, name: String,
}, },
/// An error that occurs when the URL in the source is invalid. /// An error that occurs when the URL in the source is invalid.
#[error("invalid URL in source `{given}`")]
InvalidUrl { InvalidUrl {
/// The source string given.
given: String,
/// The URL parse error. /// The URL parse error.
#[source]
err: url::ParseError, err: url::ParseError,
}, },
/// An error that occurs when a Git URL is missing a precise commit SHA. /// An error that occurs when a Git URL is missing a precise commit SHA.
MissingSha, #[error("missing SHA in source `{given}`")]
MissingSha {
/// The source string given.
given: String,
},
/// An error that occurs when a Git URL has an invalid SHA. /// An error that occurs when a Git URL has an invalid SHA.
InvalidSha, #[error("invalid SHA in source `{given}`")]
InvalidSha {
/// The source string given.
given: String,
},
} }
/// An error that occurs when a hash digest could not be parsed. /// An error that occurs when a hash digest could not be parsed.