Skip to content

Commit 187383c

Browse files
committed
refactor: move encodable structs
1 parent af19641 commit 187383c

File tree

6 files changed

+262
-210
lines changed

6 files changed

+262
-210
lines changed

crates/cargo-util-schemas/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub mod core;
1212
pub mod index;
1313
pub mod manifest;
1414
pub mod messages;
15+
pub mod resolve;
1516
#[cfg(feature = "unstable-schema")]
1617
pub mod schema;
1718

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
use std::cmp::Ordering;
2+
use std::collections::BTreeMap;
3+
use std::fmt;
4+
use std::str::FromStr;
5+
6+
use serde::{Deserialize, Serialize, de, ser};
7+
use url::Url;
8+
9+
use crate::core::{GitReference, SourceKind};
10+
11+
/// The `Cargo.lock` structure.
12+
#[derive(Serialize, Deserialize, Debug)]
13+
pub struct EncodableResolve {
14+
pub version: Option<u32>,
15+
pub package: Option<Vec<EncodableDependency>>,
16+
/// `root` is optional to allow backward compatibility.
17+
pub root: Option<EncodableDependency>,
18+
pub metadata: Option<Metadata>,
19+
#[serde(default, skip_serializing_if = "Patch::is_empty")]
20+
pub patch: Patch,
21+
}
22+
23+
#[derive(Serialize, Deserialize, Debug, Default)]
24+
pub struct Patch {
25+
pub unused: Vec<EncodableDependency>,
26+
}
27+
28+
impl Patch {
29+
fn is_empty(&self) -> bool {
30+
self.unused.is_empty()
31+
}
32+
}
33+
34+
pub type Metadata = BTreeMap<String, String>;
35+
36+
#[derive(Serialize, Deserialize, Debug, PartialOrd, Ord, PartialEq, Eq)]
37+
pub struct EncodableDependency {
38+
pub name: String,
39+
pub version: String,
40+
pub source: Option<EncodableSourceId>,
41+
pub checksum: Option<String>,
42+
pub dependencies: Option<Vec<EncodablePackageId>>,
43+
pub replace: Option<EncodablePackageId>,
44+
}
45+
46+
#[derive(Debug, Clone)]
47+
pub struct EncodableSourceId {
48+
/// Full string of the source
49+
pub source_str: String,
50+
/// Used for sources ordering
51+
pub kind: SourceKind,
52+
/// Used for sources ordering
53+
pub url: Url,
54+
}
55+
56+
#[derive(Debug, thiserror::Error)]
57+
pub enum EncodableSourceIdParseError {
58+
#[error("invalid source `{0}`")]
59+
InvalidSource(String),
60+
61+
#[error("invalid url `{url}`: {msg}; try using `{suggest}` instead")]
62+
InvalidSourceSuggest {
63+
url: String,
64+
msg: String,
65+
suggest: String,
66+
},
67+
68+
#[error("invalid url `{url}`: {msg}")]
69+
InvalidUrl { url: String, msg: String },
70+
71+
#[error("unsupported source protocol: {0}")]
72+
UnsupportedSource(String),
73+
}
74+
75+
impl EncodableSourceId {
76+
pub fn new(source: String) -> Result<Self, EncodableSourceIdParseError> {
77+
let source_str = source.clone();
78+
let (kind, url) = source
79+
.split_once('+')
80+
.ok_or_else(|| EncodableSourceIdParseError::InvalidSource(source.clone()))?;
81+
82+
let url = {
83+
let this = url;
84+
Url::parse(this).map_err(|s| {
85+
if this.starts_with("git@") {
86+
EncodableSourceIdParseError::InvalidSourceSuggest {
87+
url: this.to_string(),
88+
msg: s.to_string(),
89+
suggest: format!("ssh://{}", this.replacen(':', "/", 1)),
90+
}
91+
} else {
92+
EncodableSourceIdParseError::InvalidUrl {
93+
url: this.to_string(),
94+
msg: s.to_string(),
95+
}
96+
}
97+
})
98+
}?;
99+
100+
let kind = match kind {
101+
"git" => {
102+
let reference = GitReference::from_query(url.query_pairs());
103+
SourceKind::Git(reference)
104+
}
105+
"registry" => SourceKind::Registry,
106+
"sparse" => SourceKind::SparseRegistry,
107+
"path" => SourceKind::Path,
108+
kind => {
109+
return Err(EncodableSourceIdParseError::UnsupportedSource(
110+
kind.to_string(),
111+
));
112+
}
113+
};
114+
115+
Ok(Self {
116+
source_str,
117+
kind,
118+
url,
119+
})
120+
}
121+
122+
pub fn as_url(&self) -> impl fmt::Display + '_ {
123+
self.source_str.clone()
124+
}
125+
}
126+
127+
impl ser::Serialize for EncodableSourceId {
128+
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
129+
where
130+
S: ser::Serializer,
131+
{
132+
s.collect_str(&self.as_url())
133+
}
134+
}
135+
136+
impl<'de> de::Deserialize<'de> for EncodableSourceId {
137+
fn deserialize<D>(d: D) -> Result<Self, D::Error>
138+
where
139+
D: de::Deserializer<'de>,
140+
{
141+
let s = String::deserialize(d)?;
142+
Ok(EncodableSourceId::new(s).map_err(de::Error::custom)?)
143+
}
144+
}
145+
146+
impl std::hash::Hash for EncodableSourceId {
147+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
148+
self.kind.hash(state);
149+
self.url.hash(state);
150+
}
151+
}
152+
153+
impl std::cmp::PartialEq for EncodableSourceId {
154+
fn eq(&self, other: &Self) -> bool {
155+
self.url == other.url && self.kind == other.kind
156+
}
157+
}
158+
159+
impl std::cmp::Eq for EncodableSourceId {}
160+
161+
impl PartialOrd for EncodableSourceId {
162+
fn partial_cmp(&self, other: &EncodableSourceId) -> Option<Ordering> {
163+
Some(self.cmp(other))
164+
}
165+
}
166+
167+
impl Ord for EncodableSourceId {
168+
fn cmp(&self, other: &EncodableSourceId) -> Ordering {
169+
self.kind
170+
.cmp(&other.kind)
171+
.then_with(|| self.url.cmp(&other.url))
172+
}
173+
}
174+
175+
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
176+
pub struct EncodablePackageId {
177+
pub name: String,
178+
pub version: Option<String>,
179+
pub source: Option<EncodableSourceId>,
180+
}
181+
182+
impl fmt::Display for EncodablePackageId {
183+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184+
write!(f, "{}", self.name)?;
185+
if let Some(s) = &self.version {
186+
write!(f, " {}", s)?;
187+
}
188+
if let Some(s) = &self.source {
189+
write!(f, " ({})", s.as_url())?;
190+
}
191+
Ok(())
192+
}
193+
}
194+
195+
#[derive(Debug, thiserror::Error)]
196+
pub enum EncodablePackageIdParseError {
197+
#[error("invalid serialied PackageId")]
198+
InvalidSerializedPackageId,
199+
200+
#[error(transparent)]
201+
Source(#[from] EncodableSourceIdParseError),
202+
}
203+
204+
impl FromStr for EncodablePackageId {
205+
type Err = EncodablePackageIdParseError;
206+
207+
fn from_str(s: &str) -> Result<EncodablePackageId, Self::Err> {
208+
let mut s = s.splitn(3, ' ');
209+
let name = s.next().unwrap();
210+
let version = s.next();
211+
let source_id = match s.next() {
212+
Some(s) => {
213+
if let Some(s) = s.strip_prefix('(').and_then(|s| s.strip_suffix(')')) {
214+
Some(EncodableSourceId::new(s.to_string())?)
215+
} else {
216+
return Err(EncodablePackageIdParseError::InvalidSerializedPackageId);
217+
}
218+
}
219+
None => None,
220+
};
221+
222+
Ok(EncodablePackageId {
223+
name: name.to_string(),
224+
version: version.map(|v| v.to_string()),
225+
source: source_id,
226+
})
227+
}
228+
}
229+
230+
impl ser::Serialize for EncodablePackageId {
231+
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
232+
where
233+
S: ser::Serializer,
234+
{
235+
s.collect_str(self)
236+
}
237+
}
238+
239+
impl<'de> de::Deserialize<'de> for EncodablePackageId {
240+
fn deserialize<D>(d: D) -> Result<EncodablePackageId, D::Error>
241+
where
242+
D: de::Deserializer<'de>,
243+
{
244+
String::deserialize(d).and_then(|string| {
245+
string
246+
.parse::<EncodablePackageId>()
247+
.map_err(de::Error::custom)
248+
})
249+
}
250+
}

0 commit comments

Comments
 (0)