magi-code 0.80.0

Repository-aware CLI coding agent for terminal work
Documentation
use super::*;
use std::io::{Seek, Write};

/// Batch ordinary UTF-8 blobs. Exceptional filenames, binary encoding, and
/// oversized output retain the individual bounded reader's notices and limits.
pub(super) fn read_originals(
    root: &Path,
    head: &str,
    paths: &BTreeMap<String, bool>,
    cache: &SnapshotCache,
    options: &ReviewReadOptions<'_>,
) -> Result<BTreeMap<String, String>, String> {
    let requested: Vec<_> = paths
        .iter()
        .take(MAX_FILES)
        .filter(|(path, original)| {
            **original
                && !path.contains(['\n', '\r'])
                && !cache
                    .files
                    .iter()
                    .any(|file| file.path == **path && file.notice.is_none())
        })
        .map(|(path, _)| path)
        .collect();
    if requested.is_empty() {
        return Ok(BTreeMap::new());
    }
    // A bounded temporary input avoids blocking on a full stdin pipe while Git
    // is waiting for its stdout to be drained. No extra writer thread is needed.
    let mut input = tempfile::tempfile().map_err(|error| error.to_string())?;
    for path in &requested {
        options.check()?;
        validate_path(path)?;
        writeln!(input, "{}:{path}", head.trim_end()).map_err(|error| error.to_string())?;
    }
    input.rewind().map_err(|error| error.to_string())?;
    let output = run_with_input(
        root,
        &["cat-file", "--batch"],
        MAX_TOTAL_SOURCE + MAX_FILES * 128,
        options,
        input.into(),
    )?;
    if output.stdout_truncated
        || !output.status.is_some_and(|status| status.success())
        || output.stdout.contains('\u{fffd}')
    {
        return Ok(BTreeMap::new());
    }
    Ok(parse_originals(&output.stdout, &requested).unwrap_or_default())
}

fn parse_originals(output: &str, paths: &[&String]) -> Option<BTreeMap<String, String>> {
    let mut remaining = output;
    let mut originals = BTreeMap::new();
    for path in paths {
        let (header, rest) = remaining.split_once('\n')?;
        let mut fields = header.split_whitespace();
        let _object = fields.next()?;
        let kind = fields.next()?;
        let size: usize = fields.next()?.parse().ok()?;
        if fields.next().is_some() {
            return None;
        }
        let content = rest.get(..size)?;
        remaining = rest.get(size..)?.strip_prefix('\n')?;
        if kind == "blob" && size <= MAX_SOURCE {
            originals.insert((*path).clone(), content.to_owned());
        }
    }
    remaining.is_empty().then_some(originals)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn batch_framing_preserves_embedded_headers_newlines_and_binary_markers() {
        let paths = ["one.rs".to_owned(), "two.rs".to_owned()];
        let first = "fake blob 123\n\0\n";
        let second = "fn main() {}";
        let output = format!(
            "abc blob {}\n{first}\ndef blob {}\n{second}\n",
            first.len(),
            second.len()
        );
        let parsed = parse_originals(&output, &[&paths[0], &paths[1]]).unwrap();
        assert_eq!(parsed[&paths[0]], first);
        assert_eq!(parsed[&paths[1]], second);
        assert!(parse_originals(&output[..output.len() - 1], &[&paths[0], &paths[1]]).is_none());
    }
}