Skip to main content

thin_fetch/command/
fetch.rs

1use ureq::BodyReader;
2
3use crate::{
4    Command, Error, HashKind, PackDecoder, WithError, bail,
5    command::Execute,
6    parcel::{ParcelDecoder, ParcelItem},
7};
8
9/// Fetch remote objects.
10///
11/// ## Example
12///
13/// ```
14/// # use thin_fetch::command::Fetch;
15/// # use thin_fetch::HashKind;
16/// # let want = "HEAD";
17/// # let have = "awawa";
18/// # let blobs = false;
19/// let fetch = Fetch {
20///     want: vec![want.to_owned()],
21///     have: vec![have.to_owned()],
22///     filter: if blobs {
23///         None
24///     } else {
25///         Some("blob:none".to_owned())
26///     },
27///     deepen: None,
28///     hash_kind: HashKind::Sha1,
29/// };
30/// ```
31#[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}