leo_package/
dependency.rs1use crate::Location;
18use std::fmt::Display;
19
20use serde::{Deserialize, Serialize};
21use std::path::PathBuf;
22
23#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
25pub struct Dependency {
26 pub name: String,
29 pub location: Location,
30 pub path: Option<PathBuf>,
32 pub edition: Option<u16>,
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub git: Option<GitSource>,
37}
38
39#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
41pub struct GitSource {
42 pub url: String,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub branch: Option<String>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub tag: Option<String>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub rev: Option<String>,
52}
53
54impl GitSource {
55 pub fn reference(&self) -> Result<GitReference, &'static str> {
57 GitReference::from_opts(&self.branch, &self.tag, &self.rev)
58 }
59}
60
61#[derive(Debug, Clone, Eq, PartialEq, Hash)]
63pub enum GitReference {
64 Branch(String),
66 Tag(String),
68 Rev(String),
70 DefaultBranch,
72}
73
74impl GitReference {
75 pub fn is_mutable(&self) -> bool {
77 matches!(self, GitReference::Branch(_) | GitReference::DefaultBranch)
78 }
79
80 pub fn lock_string(&self) -> String {
82 match self {
83 GitReference::Branch(b) => format!("branch={b}"),
84 GitReference::Tag(t) => format!("tag={t}"),
85 GitReference::Rev(r) => format!("rev={r}"),
86 GitReference::DefaultBranch => "default".to_string(),
87 }
88 }
89
90 pub fn from_opts(
93 branch: &Option<String>,
94 tag: &Option<String>,
95 rev: &Option<String>,
96 ) -> Result<GitReference, &'static str> {
97 match (branch, tag, rev) {
98 (Some(branch), None, None) => Ok(GitReference::Branch(branch.clone())),
99 (None, Some(tag), None) => Ok(GitReference::Tag(tag.clone())),
100 (None, None, Some(rev)) if is_commit_hash(rev) => Ok(GitReference::Rev(rev.clone())),
101 (None, None, Some(_)) => {
102 Err("a git `rev` must be a commit hash; use `branch` or `tag` for a named reference")
103 }
104 (None, None, None) => Ok(GitReference::DefaultBranch),
105 _ => Err("a git dependency may specify at most one of `branch`, `tag`, or `rev`"),
106 }
107 }
108}
109
110fn is_commit_hash(s: &str) -> bool {
112 (4..=64).contains(&s.len()) && s.bytes().all(|b| b.is_ascii_hexdigit())
113}
114
115impl Display for Dependency {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 write!(f, "{} (on {:?})", self.name, self.location)?;
118 if let Some(path) = &self.path {
119 write!(f, " (at {})", path.display())?;
120 }
121 if let Some(edition) = self.edition {
122 write!(f, " (edition {edition})")?;
123 }
124 if let Some(git) = &self.git {
125 write!(f, " (git {})", git.url)?;
126 if let Some(branch) = &git.branch {
127 write!(f, " (branch {branch})")?;
128 }
129 if let Some(tag) = &git.tag {
130 write!(f, " (tag {tag})")?;
131 }
132 if let Some(rev) = &git.rev {
133 write!(f, " (rev {rev})")?;
134 }
135 }
136 Ok(())
137 }
138}