use crate::errors::*;
use crate::plot::Artifacts;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::io::Cursor;
use std::io::Read;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "extract", rename_all = "kebab-case")]
pub enum ExtractArtifact {
Zip(GenericExtractArtifact),
}
impl ExtractArtifact {
pub fn resolve(&self, artifacts: &mut Artifacts) -> Result<Vec<u8>> {
match self {
ExtractArtifact::Zip(extract) => {
extract.log();
let artifact = artifacts.get(&extract.artifact).with_context(|| {
anyhow!(
"Referencing artifact that doesn't exist: {:?}",
extract.artifact
)
})?;
let cursor = Cursor::new(artifact.as_ref());
let mut archive = zip::ZipArchive::new(cursor).with_context(|| {
anyhow!(
"Failed to open artifact {:?} as zip archive",
extract.artifact
)
})?;
for i in 0..archive.len() {
trace!("Accessing zip entry {}/{}", i, archive.len());
let mut entry = archive
.by_index(i)
.with_context(|| anyhow!("Failed to access entry at index {:?}", i))?;
let name = entry.name();
if entry.is_dir() {
trace!("Skipping directory in zip file: {:?}", name);
continue;
}
if !extract.matches(name)? {
trace!("Skipping file in zip file: {:?}", name);
continue;
}
debug!("Extracting {:?} from archive...", name);
let mut buf = Vec::new();
entry
.read_to_end(&mut buf)
.context("Failed to extract file from zip")?;
return Ok(buf);
}
bail!("Archive did not contain any matching file");
}
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GenericExtractArtifact {
pub artifact: String,
pub path: Option<String>,
pub regex: Option<String>,
}
impl GenericExtractArtifact {
pub fn log(&self) {
if let Some(path) = &self.path {
info!(
"Extracting from artifact {:?} with path={:?}...",
self.artifact, path
);
} else if let Some(regex) = &self.regex {
info!(
"Extracting from artifact {:?} with regex {:?}...",
self.artifact, regex
);
} else {
info!(
"Extracting first entry from artifact {:?}...",
self.artifact
);
}
}
pub fn matches(&self, name: &str) -> Result<bool> {
if let Some(path) = &self.path {
Ok(path == name)
} else if let Some(regex) = &self.regex {
let re = Regex::new(regex)
.with_context(|| anyhow!("Failed to compile regex: {:?}", regex))?;
Ok(re.is_match(name))
} else {
Ok(true)
}
}
}