#
cargo_util_schemas/core/
source_kind.rs

    1use std::cmp::Ordering;
    2
    3/// The possible kinds of code source.
    4#[derive(Debug, Clone, PartialEq, Eq)]
    5pub enum SourceKind {
    6    /// A git repository.
    7    Git(GitReference),
    8    /// A local path.
    9    Path,
    10    /// A remote registry.
    11    Registry,
    12    /// A sparse registry.
    13    SparseRegistry,
    14    /// A local filesystem-based registry.
    15    LocalRegistry,
    16    /// A directory-based registry.
    17    Directory,
    18}
    19
    20// The hash here is important for what folder packages get downloaded into.
    21// Changes trigger all users to download another copy of their crates.
    22// So the `stable_hash` test checks that we only change it intentionally.
    23// We implement hash manually to callout the stability impact.
    24impl std::hash::Hash for SourceKind {
    25    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
    26        core::mem::discriminant(self).hash(state);
    27        if let SourceKind::Git(git) = self {
    28            git.hash(state);
    29        }
    30    }
    31}
    32
    33impl SourceKind {
    34    pub fn protocol(&self) -> Option<&str> {
    35        match self {
    36            SourceKind::Path => Some("path"),
    37            SourceKind::Git(_) => Some("git"),
    38            SourceKind::Registry => Some("registry"),
    39            // Sparse registry URL already includes the `sparse+` prefix, see `SourceId::new`
    40            SourceKind::SparseRegistry => None,
    41            SourceKind::LocalRegistry => Some("local-registry"),
    42            SourceKind::Directory => Some("directory"),
    43        }
    44    }
    45}
    46
    47// The ordering here is important for how packages are serialized into lock files.
    48// We implement it manually to callout the stability guarantee.
    49// See https://github.com/rust-lang/cargo/pull/9397 for the history.
    50impl Ord for SourceKind {
    51    fn cmp(&self, other: &SourceKind) -> Ordering {
    52        match (self, other) {
    53            (SourceKind::Path, SourceKind::Path) => Ordering::Equal,
    54            (SourceKind::Path, _) => Ordering::Less,
    55            (_, SourceKind::Path) => Ordering::Greater,
    56
    57            (SourceKind::Registry, SourceKind::Registry) => Ordering::Equal,
    58            (SourceKind::Registry, _) => Ordering::Less,
    59            (_, SourceKind::Registry) => Ordering::Greater,
    60
    61            (SourceKind::SparseRegistry, SourceKind::SparseRegistry) => Ordering::Equal,
    62            (SourceKind::SparseRegistry, _) => Ordering::Less,
    63            (_, SourceKind::SparseRegistry) => Ordering::Greater,
    64
    65            (SourceKind::LocalRegistry, SourceKind::LocalRegistry) => Ordering::Equal,
    66            (SourceKind::LocalRegistry, _) => Ordering::Less,
    67            (_, SourceKind::LocalRegistry) => Ordering::Greater,
    68
    69            (SourceKind::Directory, SourceKind::Directory) => Ordering::Equal,
    70            (SourceKind::Directory, _) => Ordering::Less,
    71            (_, SourceKind::Directory) => Ordering::Greater,
    72
    73            (SourceKind::Git(a), SourceKind::Git(b)) => a.cmp(b),
    74        }
    75    }
    76}
    77
    78/// Forwards to `Ord`
    79impl PartialOrd for SourceKind {
    80    fn partial_cmp(&self, other: &SourceKind) -> Option<Ordering> {
    81        Some(self.cmp(other))
    82    }
    83}
    84
    85/// Information to find a specific commit in a Git repository.
    86#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
    87pub enum GitReference {
    88    /// From a tag.
    89    Tag(String),
    90    /// From a branch.
    91    Branch(String),
    92    /// From a specific revision. Can be a commit hash (either short or full),
    93    /// or a named reference like `refs/pull/493/head`.
    94    Rev(String),
    95    /// The default branch of the repository, the reference named `HEAD`.
    96    DefaultBranch,
    97}
    98
    99impl GitReference {
    100    pub fn from_query(
    101        query_pairs: impl Iterator<Item = (impl AsRef<str>, impl AsRef<str>)>,
    102    ) -> Self {
    103        let mut reference = GitReference::DefaultBranch;
    104        for (k, v) in query_pairs {
    105            let v = v.as_ref();
    106            match k.as_ref() {
    107                // Map older 'ref' to branch.
    108                "branch" | "ref" => reference = GitReference::Branch(v.to_owned()),
    109
    110                "rev" => reference = GitReference::Rev(v.to_owned()),
    111                "tag" => reference = GitReference::Tag(v.to_owned()),
    112                _ => {}
    113            }
    114        }
    115        reference
    116    }
    117
    118    /// Returns a `Display`able view of this git reference, or None if using
    119    /// the head of the default branch
    120    pub fn pretty_ref(&self, url_encoded: bool) -> Option<PrettyRef<'_>> {
    121        match self {
    122            GitReference::DefaultBranch => None,
    123            _ => Some(PrettyRef {
    124                inner: self,
    125                url_encoded,
    126            }),
    127        }
    128    }
    129}
    130
    131/// A git reference that can be `Display`ed
    132pub struct PrettyRef<'a> {
    133    inner: &'a GitReference,
    134    url_encoded: bool,
    135}
    136
    137impl<'a> std::fmt::Display for PrettyRef<'a> {
    138    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    139        let value: &str;
    140        match self.inner {
    141            GitReference::Branch(s) => {
    142                write!(f, "branch=")?;
    143                value = s;
    144            }
    145            GitReference::Tag(s) => {
    146                write!(f, "tag=")?;
    147                value = s;
    148            }
    149            GitReference::Rev(s) => {
    150                write!(f, "rev=")?;
    151                value = s;
    152            }
    153            GitReference::DefaultBranch => unreachable!(),
    154        }
    155        if self.url_encoded {
    156            for value in url::form_urlencoded::byte_serialize(value.as_bytes()) {
    157                write!(f, "{value}")?;
    158            }
    159        } else {
    160            write!(f, "{value}")?;
    161        }
    162        Ok(())
    163    }
    164}