use toml;
use difference::Changeset;
use std::collections::BTreeMap;
use dev_prefix::*;
use types::*;
use user::types::*;
#[derive(Debug, PartialEq)]
pub struct ProjectText {
pub origin: PathBuf,
pub files: HashMap<PathBuf, String>,
}
pub enum PathDiff {
DoesNotExist,
NotUtf8,
Changeset(Changeset),
None,
}
impl Default for ProjectText {
fn default() -> ProjectText {
ProjectText {
origin: PathBuf::from("INVALID-ORIGIN"),
files: HashMap::default(),
}
}
}
impl ProjectText {
pub fn from_project(project: &Project) -> Result<ProjectText> {
let mut files: HashMap<PathBuf, BTreeMap<String, UserArtifact>> = HashMap::new();
for (name, artifact) in &project.artifacts {
if !files.contains_key(&artifact.def) {
files.insert(artifact.def.clone(), BTreeMap::new());
}
let tbl = files.get_mut(&artifact.def).unwrap();
let partof = {
let mut auto_partof = name.named_partofs();
if !name.is_root() {
auto_partof.push(name.parent().expect("no parent"));
}
let auto_partof: HashSet<Name> = HashSet::from_iter(auto_partof.drain(0..));
let mut strs = artifact
.partof
.iter()
.filter(|p| !auto_partof.contains(p))
.map(|p| p.raw.clone())
.collect::<Vec<_>>();
if strs.is_empty() {
None
} else if strs.len() == 1 {
let s = strs.drain(0..).next().unwrap();
Some(UserPartof::Single(s))
} else {
strs.sort();
Some(UserPartof::Multi(strs))
}
};
let raw = UserArtifact {
partof: partof,
text: if artifact.text.is_empty() {
None
} else {
Some(artifact.text.clone())
},
done: if let Done::Defined(ref d) = artifact.done {
Some(d.clone())
} else {
None
},
};
tbl.insert(name.raw.clone(), raw);
}
let mut text: HashMap<PathBuf, String> = HashMap::new();
for (p, v) in files.drain() {
let mut s = String::new();
v.serialize(&mut toml::Serializer::pretty(&mut s))
.expect("serialize");
text.insert(p, s);
}
for p in &project.files {
if !text.contains_key(p) {
text.insert(p.clone(), String::with_capacity(0));
}
}
Ok(ProjectText {
files: text,
origin: project.origin.clone(),
})
}
pub fn dump(&self) -> Result<()> {
for (path, text) in &self.files {
debug!("Writing to {}", path.display());
if let Err(err) = fs::create_dir_all(path.parent().expect("path not file")) {
match err.kind() {
io::ErrorKind::AlreadyExists => {}
_ => return Err(err.into()),
}
}
let mut f = fs::File::create(path)?;
f.write_all(text.as_bytes())?;
}
Ok(())
}
pub fn diff(&self) -> Result<HashMap<PathBuf, PathDiff>> {
let mut out: HashMap<PathBuf, PathDiff> = HashMap::new();
for (path, text) in &self.files {
debug!("Diffing: {}", path.display());
let mut f = match fs::File::open(path) {
Ok(f) => f,
Err(_) => {
out.insert(path.clone(), PathDiff::DoesNotExist);
continue;
}
};
let mut bytes = Vec::new();
f.read_to_end(&mut bytes)?;
let original = match str::from_utf8(&bytes) {
Ok(s) => s,
Err(_) => {
out.insert(path.clone(), PathDiff::NotUtf8);
continue;
}
};
let ch = Changeset::new(original, text, "\n");
let d = if ch.distance == 0 {
PathDiff::None
} else {
PathDiff::Changeset(ch)
};
out.insert(path.clone(), d);
}
Ok(out)
}
}