tytanic_core/project/
vcs.rsuse std::fmt::{self, Debug, Display};
use std::path::{Path, PathBuf};
use std::{fs, io};
use super::Paths;
use crate::test::Test;
const GITIGNORE_NAME: &str = ".gitignore";
const HGIGNORE_NAME: &str = ".hgignore";
const IGNORE_HEADER: &str = "# generated by tytanic, do not edit";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Kind {
Git,
Mercurial,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Vcs {
root: PathBuf,
kind: Kind,
}
impl Vcs {
pub fn new<I>(root: I, kind: Kind) -> Self
where
I: Into<PathBuf>,
{
Self {
root: root.into(),
kind,
}
}
pub fn try_new(root: &Path) -> io::Result<Option<Self>> {
if root.join(".git").try_exists()? || root.join(".jj").try_exists()? {
Ok(Some(Self::new(root, Kind::Git)))
} else if root.join(".hg").try_exists()? {
Ok(Some(Self::new(root, Kind::Mercurial)))
} else {
Ok(None)
}
}
}
impl Vcs {
pub fn root(&self) -> &Path {
&self.root
}
pub fn kind(&self) -> Kind {
self.kind
}
pub fn ignore(&self, paths: &Paths, test: &Test) -> io::Result<()> {
let mut content = format!("{IGNORE_HEADER}\n\n");
let file = paths.test_dir(test.id()).join(match self.kind {
Kind::Git => GITIGNORE_NAME,
Kind::Mercurial => {
content.push_str("syntax: glob\n");
HGIGNORE_NAME
}
});
for always in ["diff/**\n", "out/**\n"] {
content.push_str(always);
}
if !test.kind().is_persistent() {
content.push_str("ref/**\n");
}
fs::write(file, content)?;
Ok(())
}
pub fn unignore(&self, paths: &Paths, test: &Test) -> io::Result<()> {
let file = paths.test_dir(test.id()).join(match self.kind {
Kind::Git => GITIGNORE_NAME,
Kind::Mercurial => HGIGNORE_NAME,
});
fs::remove_file(file)
}
}
impl Display for Vcs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(match self.kind {
Kind::Git => "Git",
Kind::Mercurial => "Mercurial",
})
}
}
#[cfg(test)]
mod tests {
use tytanic_utils::fs::TempTestEnv;
use super::*;
use crate::project::Paths;
use crate::test::{Id, Kind as TestKind};
fn test(kind: TestKind) -> Test {
Test::new_test(Id::new("fancy").unwrap(), kind)
}
#[test]
fn test_git_ignore_create() {
TempTestEnv::run(
|root| root.setup_dir("tests/fancy"),
|root| {
let paths = Paths::new(root, None);
let vcs = Vcs::new(root, Kind::Git);
let test = test(TestKind::CompileOnly);
vcs.ignore(&paths, &test).unwrap();
},
|root| {
root.expect_dir("tests/fancy").expect_file_content(
"tests/fancy/.gitignore",
format!("{IGNORE_HEADER}\n\ndiff/**\nout/**\nref/**\n"),
)
},
);
}
#[test]
fn test_git_ignore_truncate() {
TempTestEnv::run(
|root| root.setup_file("tests/fancy/.gitignore", "blah blah"),
|root| {
let paths = Paths::new(root, None);
let vcs = Vcs::new(root, Kind::Git);
let test = test(TestKind::CompileOnly);
vcs.ignore(&paths, &test).unwrap();
},
|root| {
root.expect_dir("tests/fancy").expect_file_content(
"tests/fancy/.gitignore",
format!("{IGNORE_HEADER}\n\ndiff/**\nout/**\nref/**\n"),
)
},
);
}
#[test]
fn test_git_unignore() {
TempTestEnv::run(
|root| root.setup_file("tests/fancy/.gitignore", "blah blah"),
|root| {
let paths = Paths::new(root, None);
let vcs = Vcs::new(root, Kind::Git);
let test = test(TestKind::CompileOnly);
vcs.unignore(&paths, &test).unwrap();
},
|root| root.expect_dir("tests/fancy"),
);
}
}