thin_fetch/command/
fetch.rs1use ureq::BodyReader;
2
3use crate::{
4 Command, Error, HashKind, PackDecoder, WithError, bail,
5 command::Execute,
6 parcel::{ParcelDecoder, ParcelItem},
7};
8
9#[derive(Default, Debug)]
32pub struct Fetch {
33 pub want: Vec<String>,
34 pub have: Vec<String>,
35 pub filter: Option<String>,
36 pub deepen: Option<usize>,
37 pub hash_kind: HashKind,
38 pub include_tag: bool,
39}
40
41impl From<&Fetch> for Command {
42 fn from(value: &Fetch) -> Self {
43 let mut cmd = Command::new("fetch").object_format(value.hash_kind);
44
45 for want in &value.want {
46 cmd = cmd.arg(format!("want {want}"));
47 }
48 for have in &value.have {
49 cmd = cmd.arg(format!("have {have}"));
50 }
51
52 if let Some(filter) = &value.filter {
53 cmd = cmd.arg(format!("filter {filter}"));
54 }
55 if let Some(deepen) = &value.deepen {
56 cmd = cmd.arg(format!("deepen {deepen}"));
57 }
58 if value.include_tag {
59 cmd = cmd.arg("include-tag");
60 }
61
62 cmd = cmd.arg("done");
63
64 cmd
65 }
66}
67
68impl Execute for Fetch {
69 type Output = PackDecoder<FetchDecoder<BodyReader<'static>>>;
70
71 fn execute(&self, repo: &crate::Repo) -> Result<Self::Output, Error> {
72 let parcel = repo.stream(&self.into())?;
73 let fetch = FetchDecoder::new(parcel);
74 let pack = PackDecoder::new(fetch, self.hash_kind)?;
75 Ok(pack)
76 }
77}
78
79#[derive(PartialEq, Eq)]
80enum FetchMode {
81 Initial,
82 Packfile,
83 ShallowInfo,
84}
85
86pub struct FetchDecoder<R: std::io::Read> {
87 mode: FetchMode,
88 pack_data: Vec<u8>,
89 reader: ParcelDecoder<R>,
90}
91
92impl<R: std::io::Read> FetchDecoder<R> {
93 fn new(reader: ParcelDecoder<R>) -> Self {
94 Self {
95 mode: FetchMode::Initial,
96 pack_data: vec![],
97 reader,
98 }
99 }
100
101 fn read_data(&mut self) -> Result<Vec<u8>, Error> {
102 let mut data = vec![];
103
104 while data.is_empty() {
105 let item = self.reader.get_one().with_err_msg("read one item");
106 if item == Ok(ParcelItem::End) {
107 return Ok(vec![]);
108 }
109 data = self.parse_data(item)?;
110 }
111
112 Ok(data)
113 }
114
115 fn parse_data(&mut self, item: Result<ParcelItem, Error>) -> Result<Vec<u8>, Error> {
116 match item? {
117 ParcelItem::Data(data) if self.mode == FetchMode::Initial => {
118 let header = str::from_utf8(&data).with_err_msg("could not decode header")?;
119 match header {
120 "packfile\n" => {
121 self.mode = FetchMode::Packfile;
122 Ok(vec![])
123 }
124 "shallow-info\n" => {
125 self.mode = FetchMode::ShallowInfo;
126 Ok(vec![])
127 }
128 hdr => {
129 bail!("unknown header: {hdr}");
130 }
131 }
132 }
133 ParcelItem::Data(_data) if self.mode == FetchMode::ShallowInfo => {
134 Ok(vec![])
135 }
136 ParcelItem::Data(data) if self.mode == FetchMode::Packfile => {
137 let stream = data.first().with_err_msg("stream empty")?;
138
139 let data = &data[1..];
140
141 match *stream {
142 1 => Ok(data.to_vec()),
143 2 => {
144 let msg = str::from_utf8(data)
145 .with_err_msg("unexpected non-utf8 data in status stream")?;
146 print!("{msg}");
147 Ok(vec![])
148 }
149 3 => {
150 let msg = str::from_utf8(data)
151 .with_err_msg("unexpected non-utf8 data in error stream")?;
152 bail!("remote error: {msg}");
153 }
154 n => {
155 bail!("unknown stream: {n}");
156 }
157 }
158 }
159 ParcelItem::Data(_data) => {
160 todo!()
161 }
162 ParcelItem::Separator => {
163 self.mode = FetchMode::Initial;
164 Ok(vec![])
165 }
166 ParcelItem::End => {
167 bail!("unexpected data end");
168 }
169 }
170 }
171}
172
173impl<R: std::io::Read> std::io::Read for FetchDecoder<R> {
174 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
175 let data = if self.pack_data.is_empty() {
176 self.read_data()?
177 } else {
178 std::mem::take(&mut self.pack_data)
179 };
180
181 if data.is_empty() {
182 return Ok(0);
183 }
184
185 if data.len() > buf.len() {
186 let src = &data[0..buf.len()];
187 let rest = &data[buf.len()..];
188 buf.copy_from_slice(src);
189 self.pack_data.extend_from_slice(rest);
190 return Ok(buf.len());
191 }
192
193 let dst = &mut buf[0..data.len()];
194 dst.copy_from_slice(&data);
195 Ok(data.len())
196 }
197}
198
199impl<R: std::io::Read> std::io::BufRead for FetchDecoder<R> {
200 fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
201 if self.pack_data.is_empty() {
202 let data = self.read_data()?;
203 self.pack_data.extend_from_slice(&data);
204 }
205
206 Ok(&self.pack_data)
207 }
208
209 fn consume(&mut self, amount: usize) {
210 self.pack_data.drain(0..amount);
211 }
212}