carnix 0.10.3

Generate Nix expressions from Cargo.lock files (in order to use Nix as a build system for crates).
use failure::Error;
use krate::Src;
use std;
use std::collections::BTreeMap;
use std::io::{Read, Write};
use std::path::Path;
use std::path::PathBuf;

#[derive(Debug, Clone)]
pub struct Prefetch {
    pub prefetch: Src,
    pub path: PathBuf,
}

#[derive(Debug)]
pub struct Cache {
    path: PathBuf,
    cache: String,
    lines: Vec<(usize, usize)>,
    new: BTreeMap<String, Prefetch>,
}

impl Drop for Cache {
    fn drop(&mut self) {
        std::fs::create_dir_all(self.path.parent().unwrap()).unwrap();
        let mut path2 = self.path.clone();
        path2.set_extension("tmp");
        let mut f = std::fs::File::create(&path2).unwrap();
        let mut new_it = self.new.iter().peekable();
        let mut ex_it = self.lines.iter().peekable();
        loop {
            let advance_new = if let Some((a, b)) = new_it.peek() {
                if let Some((c, d)) = ex_it.peek() {
                    let (u, v, w) = self.get_index(*c, *d).unwrap();
                    if u < a {
                        writeln!(f, "{} {} {}", u, v, w).unwrap();
                        false
                    } else {
                        if let Src::Crate { ref sha256 } = b.prefetch {
                            writeln!(f, "{} {} {}", a, sha256, b.path.to_string_lossy()).unwrap();
                        }
                        true
                    }
                } else {
                    if let Src::Crate { ref sha256 } = b.prefetch {
                        writeln!(f, "{} {} {}", a, sha256, b.path.to_string_lossy()).unwrap();
                    }
                    true
                }
            } else {
                for &(l0, l1) in ex_it {
                    let (u, v, w) = self.get_index(l0, l1).unwrap();
                    writeln!(f, "{} {} {}", u, v, w).unwrap();
                }
                break;
            };
            if advance_new {
                new_it.next();
            } else {
                ex_it.next();
            }
        }
        std::fs::rename(&path2, &self.path).unwrap();
    }
}

impl Cache {
    pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
        let path = path.as_ref();
        let mut cache = String::new();
        if let Ok(mut f) = std::fs::File::open(path) {
            f.read_to_string(&mut cache)?;
        }
        let mut lines = Vec::new();
        for l in cache.lines() {
            if lines.is_empty() {
                lines.reserve(2 * cache.len() / l.len());
            }
            let l0 = l.as_ptr() as usize - cache.as_ptr() as usize;
            lines.push((l0, l0 + l.len()))
        }
        Ok(Cache {
            path: path.to_path_buf(),
            cache,
            lines,
            new: BTreeMap::new(),
        })
    }

    fn get_index(&self, l0: usize, l1: usize) -> Option<(&str, &str, &str)> {
        let a = self.cache.get(l0..l1).unwrap();
        let mut it = a.split(' ');
        if let (Some(u), Some(v), Some(w)) = (it.next(), it.next(), it.next()) {
            Some((u, v, w))
        } else {
            None
        }
    }

    pub fn get(&mut self, url: &str) -> Option<Prefetch> {
        debug!("cache get {:?}", url);
        if let Some(pre) = self.new.get(url) {
            Some(pre.clone())
        } else {
            let n = match self.lines.binary_search_by(|&(a, b)| {
                let url0 = self.cache.get(a..b).unwrap();
                debug!("{:?} {:?}", url, url0);
                if url0.starts_with(url) {
                    std::cmp::Ordering::Equal
                } else {
                    url0.cmp(url)
                }
            }) {
                Ok(n) => n,
                _ => return None,
            };
            debug!("n = {:?}", n);
            let (l0, l1) = self.lines[n];
            self.get_index(l0, l1).map(|(_, b, c)| Prefetch {
                prefetch: Src::Crate {
                    sha256: b.to_string(),
                },
                path: Path::new(c).to_path_buf(),
            })
        }
    }

    pub fn insert(&mut self, url: &str, prefetch: Prefetch) {
        self.new.insert(url.to_string(), prefetch);
    }
}