1use super::errors::*;
2use std::collections::HashMap;
3use std::path::Path;
4use url::Url;
5
6const CRATES_IO_INDEX: &str = tame_index::index::sparse::CRATES_IO_HTTP_INDEX;
7const CRATES_IO_REGISTRY: &str = "crates-io";
8
9pub fn registry_url(manifest_path: &Path, registry: Option<&str>) -> CargoResult<Url> {
11 fn read_config(
13 registries: &mut HashMap<String, Source>,
14 path: impl AsRef<Path>,
15 ) -> CargoResult<()> {
16 let path = path.as_ref();
17 let content = std::fs::read_to_string(path)?;
19 let config = toml::from_str::<CargoConfig>(&content)
20 .with_context(|| anyhow::format_err!("invalid cargo config at {}", path.display()))?;
21 for (key, value) in config.registries {
22 registries.entry(key).or_insert(Source {
23 registry: value.index,
24 replace_with: None,
25 });
26 }
27 for (key, value) in config.source {
28 registries.entry(key).or_insert(value);
29 }
30 Ok(())
31 }
32 let mut registries: HashMap<String, Source> = HashMap::new();
36 for work_dir in manifest_path
38 .parent()
39 .expect("there must be a parent directory")
40 .ancestors()
41 {
42 let work_cargo_dir = work_dir.join(".cargo");
43 let config_path = work_cargo_dir.join("config");
44 if config_path.is_file() {
45 read_config(&mut registries, config_path)?;
46 } else {
47 let config_path = work_cargo_dir.join("config.toml");
48 if config_path.is_file() {
49 read_config(&mut registries, config_path)?;
50 }
51 }
52 }
53
54 let default_cargo_home = home::cargo_home()?;
55 let default_config_path = default_cargo_home.join("config");
56 if default_config_path.is_file() {
57 read_config(&mut registries, default_config_path)?;
58 } else {
59 let default_config_path = default_cargo_home.join("config.toml");
60 if default_config_path.is_file() {
61 read_config(&mut registries, default_config_path)?;
62 }
63 }
64
65 let mut source = match registry {
67 Some(CRATES_IO_INDEX) | None => {
68 let mut source = registries.remove(CRATES_IO_REGISTRY).unwrap_or_default();
69 source
70 .registry
71 .get_or_insert_with(|| CRATES_IO_INDEX.to_string());
72 source
73 }
74 Some(r) => registries
75 .remove(r)
76 .with_context(|| anyhow::format_err!("The registry '{}' could not be found", r))?,
77 };
78
79 while let Some(replace_with) = &source.replace_with {
81 let is_crates_io = replace_with == CRATES_IO_INDEX;
82 source = registries.remove(replace_with).with_context(|| {
83 anyhow::format_err!("The source '{}' could not be found", replace_with)
84 })?;
85 if is_crates_io {
86 source
87 .registry
88 .get_or_insert_with(|| CRATES_IO_INDEX.to_string());
89 }
90 }
91
92 let registry_url = source
93 .registry
94 .ok_or_else(|| anyhow::format_err!("missing `registry`"))?;
95 let registry_url = Url::parse(®istry_url)
96 .with_context(|| anyhow::format_err!("invalid `registry` field"))?;
97
98 Ok(registry_url)
99}
100
101#[derive(Debug, Deserialize)]
102struct CargoConfig {
103 #[serde(default)]
104 registries: HashMap<String, Registry>,
105 #[serde(default)]
106 source: HashMap<String, Source>,
107}
108
109#[derive(Default, Debug, Deserialize)]
110struct Source {
111 #[serde(rename = "replace-with")]
112 replace_with: Option<String>,
113 registry: Option<String>,
114}
115
116#[derive(Debug, Deserialize)]
117struct Registry {
118 index: Option<String>,
119}
120
121mod code_from_cargo {
122 #![allow(dead_code)]
123
124 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
125 pub enum Kind {
126 Git(GitReference),
127 Path,
128 Registry,
129 LocalRegistry,
130 Directory,
131 }
132
133 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
134 pub enum GitReference {
135 Tag(String),
136 Branch(String),
137 Rev(String),
138 DefaultBranch,
139 }
140}