1use super::{
2 super::{context::*, url::*, util::*},
3 git_url::*,
4};
5
6impl URL for GitUrl {
7 fn context(&self) -> &UrlContext {
8 &*self.context
9 }
10
11 fn query(&self) -> Option<UrlQuery> {
12 self.repository_url.query()
13 }
14
15 fn fragment(&self) -> Option<String> {
16 self.repository_url.fragment()
17 }
18
19 fn format(&self) -> Option<String> {
20 get_format_from_path(&self.path)
21 }
22
23 fn base(&self) -> Option<UrlRef> {
24 get_relative_path_parent(&self.path).map(|path| self.new_with(path).into())
25 }
26
27 fn relative(&self, path: &str) -> UrlRef {
28 self.new_with(self.path.join(path)).into()
29 }
30
31 #[cfg(feature = "blocking")]
32 fn conform(&mut self) -> Result<(), super::super::UrlError> {
33 self.conform_path()
34 }
35
36 #[cfg(feature = "async")]
37 fn conform_async(&self) -> Result<ConformFuture, super::super::UrlError> {
38 use super::super::errors::*;
39
40 async fn conform_async(mut url: GitUrl) -> Result<UrlRef, UrlError> {
41 url.conform_path()?;
42 Ok(url.into())
43 }
44
45 Ok(Box::pin(conform_async(self.clone())))
46 }
47
48 #[cfg(feature = "blocking")]
49 fn open(&self) -> Result<ReadRef, super::super::UrlError> {
50 Ok(Box::new(self.open_cursor()?))
51 }
52
53 #[cfg(feature = "async")]
54 fn open_async(&self) -> Result<OpenFuture, super::super::UrlError> {
55 use super::super::errors::*;
56
57 async fn open_async(url: GitUrl) -> Result<AsyncReadRef, UrlError> {
58 Ok(Box::pin(url.open_cursor()?))
59 }
60
61 Ok(Box::pin(open_async(self.clone())))
62 }
63}
64
65#[cfg(any(feature = "blocking", feature = "async"))]
66impl GitUrl {
67 fn open_cursor(&self) -> Result<std::io::Cursor<Vec<u8>>, super::super::UrlError> {
68 use {
69 super::{super::errors::*, errors::*},
70 gix::*,
71 std::{io::Cursor, num::*},
72 tracing::info,
73 };
74
75 let (commit, ref_name) = match self.fragment() {
77 Some(fragment) => {
78 match ObjectId::from_hex(fragment.as_bytes()) {
80 Ok(object) => {
81 info!("using commit: {}", object);
82 (Some(object), None)
83 }
84
85 Err(_) => {
87 info!("using reference name: {}", fragment);
88 (None, Some(fragment))
89 }
90 }
91 }
92
93 None => (None, None),
94 };
95
96 let repository = if self.repository_gix_url.scheme == url::Scheme::File {
97 if commit.is_some() || ref_name.is_some() {
98 return Err(UrlError::UnsupportedFormat("fragment cannot be used with local git repositories".into()));
99 }
100
101 info!("opening local repository: {}", self.repository_gix_url);
103
104 let path = self.repository_gix_url.path.to_string();
105 open(path).map_err(GitError::from)?
106 } else {
107 let (directory, existing) = self.context.cache.directory(&self.repository_url.to_string(), "git-")?;
108 let directory = directory.lock()?;
109
110 if existing {
111 info!("opening cached repository: {}", directory.display());
112
113 open(directory.clone()).map_err(GitError::from)?
114 } else {
115 info!("cloning repository to: {}", directory.display());
116
117 let mut prepare_fetch = prepare_clone_bare(self.repository_gix_url.clone(), directory.clone())
118 .map_err(GitError::from)?
119 .configure_remote(|remote| Ok(remote));
120
121 if commit.is_none() {
122 let one = NonZeroU32::new(1).expect("NonZeroU32::new");
124 prepare_fetch = prepare_fetch
125 .with_shallow(remote::fetch::Shallow::DepthAtRemote(one))
126 .with_ref_name(ref_name.as_ref()) .map_err(GitError::from)?;
128 }
129
130 let (repository, _) =
131 prepare_fetch.fetch_only(progress::Discard, &interrupt::IS_INTERRUPTED).map_err(GitError::from)?;
132
133 repository
134 }
135 };
136
137 let tree = match commit {
139 Some(commit) => {
141 let commit = repository.find_commit(commit).map_err(GitError::from)?;
142 commit.tree().map_err(GitError::from)?
143 }
144
145 None => repository.head_tree().map_err(GitError::from)?,
147 };
148
149 let entry = tree
150 .lookup_entry_by_path(self.path.as_str())
151 .map_err(GitError::from)?
152 .ok_or_else(|| UrlError::new_io_not_found(self))?;
153
154 let object = entry.object().map_err(GitError::from)?;
157 let mut blob = object.try_into_blob().map_err(GitError::from)?;
158 let data = blob.take_data();
159
160 Ok(Cursor::new(data))
161 }
162}
163
164#[cfg(any(feature = "blocking", feature = "async"))]
165impl GitUrl {
166 fn conform_path(&mut self) -> Result<(), super::super::UrlError> {
167 self.path = self.path.normalize();
168 Ok(())
169 }
170}