/
dependency.rs
160 lines (143 loc) · 5.48 KB
/
dependency.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use diesel::prelude::*;
use crate::git;
use crate::util::errors::{cargo_err, AppResult};
use crate::models::{Crate, Version};
use crate::schema::*;
use crate::views::{EncodableCrateDependency, EncodableDependency};
#[derive(Identifiable, Associations, Debug, Queryable, QueryableByName)]
#[belongs_to(Version)]
#[belongs_to(Crate)]
#[table_name = "dependencies"]
pub struct Dependency {
pub id: i32,
pub version_id: i32,
pub crate_id: i32,
pub req: semver::VersionReq,
pub optional: bool,
pub default_features: bool,
pub features: Vec<String>,
pub target: Option<String>,
pub kind: DependencyKind,
}
#[derive(Debug, QueryableByName)]
pub struct ReverseDependency {
#[diesel(embed)]
dependency: Dependency,
#[sql_type = "::diesel::sql_types::Integer"]
crate_downloads: i32,
#[sql_type = "::diesel::sql_types::Text"]
#[column_name = "crate_name"]
name: String,
}
#[derive(Copy, Clone, Serialize, Deserialize, Debug, FromSqlRow)]
#[serde(rename_all = "lowercase")]
#[repr(u32)]
pub enum DependencyKind {
Normal = 0,
Build = 1,
Dev = 2,
// if you add a kind here, be sure to update `from_row` below.
}
impl Dependency {
// `downloads` need only be specified when generating a reverse dependency
pub fn encodable(self, crate_name: &str, downloads: Option<i32>) -> EncodableDependency {
EncodableDependency {
id: self.id,
version_id: self.version_id,
crate_id: crate_name.into(),
req: self.req.to_string(),
optional: self.optional,
default_features: self.default_features,
features: self.features,
target: self.target,
kind: self.kind,
downloads: downloads.unwrap_or(0),
}
}
}
impl ReverseDependency {
pub fn encodable(self, crate_name: &str) -> EncodableDependency {
self.dependency
.encodable(crate_name, Some(self.crate_downloads))
}
}
pub fn add_dependencies(
conn: &PgConnection,
deps: &[EncodableCrateDependency],
target_version_id: i32,
) -> AppResult<Vec<git::Dependency>> {
use self::dependencies::dsl::*;
use diesel::insert_into;
let git_and_new_dependencies = deps
.iter()
.map(|dep| {
if let Some(registry) = &dep.registry {
if !registry.is_empty() {
return Err(cargo_err(&format_args!("Dependency `{}` is hosted on another registry. Cross-registry dependencies are not permitted on crates.io.", &*dep.name)));
}
}
// Match only identical names to ensure the index always references the original crate name
let krate:Crate = Crate::by_exact_name(&dep.name)
.first(&*conn)
.map_err(|_| cargo_err(&format_args!("no known crate named `{}`", &*dep.name)))?;
if dep.version_req == semver::VersionReq::parse("*").unwrap() {
return Err(cargo_err(
"wildcard (`*`) dependency constraints are not allowed \
on crates.io. See https://doc.rust-lang.org/cargo/faq.html#can-\
libraries-use--as-a-version-for-their-dependencies for more \
information",
));
}
// If this dependency has an explicit name in `Cargo.toml` that
// means that the `name` we have listed is actually the package name
// that we're depending on. The `name` listed in the index is the
// Cargo.toml-written-name which is what cargo uses for
// `--extern foo=...`
let (name, package) = match &dep.explicit_name_in_toml {
Some(explicit) => (explicit.to_string(), Some(dep.name.to_string())),
None => (dep.name.to_string(), None),
};
Ok((
git::Dependency {
name,
req: dep.version_req.to_string(),
features: dep.features.iter().map(|s| s.0.to_string()).collect(),
optional: dep.optional,
default_features: dep.default_features,
target: dep.target.clone(),
kind: dep.kind.or(Some(DependencyKind::Normal)),
package,
},
(
version_id.eq(target_version_id),
crate_id.eq(krate.id),
req.eq(dep.version_req.to_string()),
dep.kind.map(|k| kind.eq(k as i32)),
optional.eq(dep.optional),
default_features.eq(dep.default_features),
features.eq(&dep.features),
target.eq(dep.target.as_deref()),
),
))
})
.collect::<Result<Vec<_>, _>>()?;
let (git_deps, new_dependencies): (Vec<_>, Vec<_>) =
git_and_new_dependencies.into_iter().unzip();
insert_into(dependencies)
.values(&new_dependencies)
.execute(conn)?;
Ok(git_deps)
}
use diesel::deserialize::{self, FromSql};
use diesel::pg::Pg;
use diesel::sql_types::Integer;
impl FromSql<Integer, Pg> for DependencyKind {
fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
match <i32 as FromSql<Integer, Pg>>::from_sql(bytes)? {
0 => Ok(DependencyKind::Normal),
1 => Ok(DependencyKind::Build),
2 => Ok(DependencyKind::Dev),
n => Err(format!("unknown kind: {}", n).into()),
}
}
}