use std::fmt::{self, Debug, Display, Formatter};
use std::fs::{self, File};
use std::io::{BufRead, BufReader};
use std::marker::PhantomData;
use std::ops::{Index, IndexMut};
use std::path::Path;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use tracing::{error, instrument};
use super::{Corpus, CorpusType, Named};
use crate::corpora::typestate::{CorpusBuildState, HasItems, HasName, NoItems, NoName};
use crate::error::FeroxFuzzError;
use crate::input::Data;
use crate::std_ext::fmt::DisplayExt;
use crate::std_ext::ops::Len;
use crate::AsInner;
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DirCorpus {
items: Vec<Data>,
corpus_name: String,
}
impl Corpus for DirCorpus {
fn add(&mut self, value: Data) {
self.items.push(value);
}
fn get(&self, index: usize) -> Option<&Data> {
self.items.get(index)
}
#[inline]
fn items(&self) -> &[Data] {
&self.items
}
}
fn from_directory<P>(directory: P) -> Result<Vec<Data>, FeroxFuzzError>
where
P: AsRef<Path>,
{
let mut items = Vec::new();
for entry in fs::read_dir(directory)? {
let entry = entry?; let path = entry.path();
let metadata = fs::metadata(&path)?;
if metadata.is_file() {
let file = File::open(&path).map_err(|source| {
error!(
?path,
"could not open file while populating the corpus: {}", source
);
FeroxFuzzError::CorpusFileOpenError {
source,
path: path.to_string_lossy().to_string(),
}
})?;
let reader = BufReader::new(file);
for line in reader.lines().map_while(Result::ok) {
if line.is_empty() || line.starts_with('#') {
continue;
}
let associated_type = line.parse().unwrap();
items.push(associated_type);
}
}
}
Ok(items)
}
impl DirCorpus {
#[must_use]
#[inline]
#[allow(clippy::new_ret_no_self)]
pub const fn new() -> DirCorpusBuilder<NoItems, NoName> {
DirCorpusBuilder {
items: Vec::new(),
corpus_name: None,
_item_state: PhantomData,
_name_state: PhantomData,
}
}
#[instrument(skip_all, level = "trace")]
pub fn from_directory<P>(
directory: P,
) -> Result<DirCorpusBuilder<HasItems, NoName>, FeroxFuzzError>
where
P: AsRef<Path>,
{
Ok(DirCorpusBuilder {
items: from_directory(directory)?,
corpus_name: None,
_item_state: PhantomData,
_name_state: PhantomData,
})
}
#[must_use]
#[inline]
pub fn items(&self) -> &[Data] {
&self.items
}
#[must_use]
#[inline]
pub fn items_mut(&mut self) -> &mut [Data] {
&mut self.items
}
#[must_use]
pub fn iter_mut(&mut self) -> <&mut [Data] as IntoIterator>::IntoIter {
<&mut Self as IntoIterator>::into_iter(self)
}
#[must_use]
pub fn iter(&self) -> <&[Data] as IntoIterator>::IntoIter {
<&Self as IntoIterator>::into_iter(self)
}
}
impl Named for DirCorpus {
fn name(&self) -> &str {
&self.corpus_name
}
}
impl Len for DirCorpus {
#[inline]
fn len(&self) -> usize {
self.items.len()
}
}
impl AsInner for DirCorpus {
type Type = Vec<Data>;
fn inner(&self) -> &Self::Type {
&self.items
}
}
impl Display for DirCorpus {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.display_top(3))
}
}
impl Index<usize> for DirCorpus {
type Output = Data;
fn index(&self, index: usize) -> &Self::Output {
&self.items()[index]
}
}
impl IndexMut<usize> for DirCorpus {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.items_mut()[index]
}
}
impl<'i> IntoIterator for &'i mut DirCorpus {
type Item = &'i mut Data;
type IntoIter = <&'i mut [Data] as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.items.iter_mut()
}
}
impl IntoIterator for DirCorpus {
type Item = Data;
type IntoIter = <Vec<Data> as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.items.into_iter()
}
}
impl<'i> IntoIterator for &'i DirCorpus {
type Item = &'i Data;
type IntoIter = <&'i [Data] as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.items.iter()
}
}
pub struct DirCorpusBuilder<IS, NS>
where
IS: CorpusBuildState,
NS: CorpusBuildState,
{
items: Vec<Data>,
corpus_name: Option<String>,
_item_state: PhantomData<IS>,
_name_state: PhantomData<NS>,
}
impl<IS> DirCorpusBuilder<IS, NoName>
where
IS: CorpusBuildState,
{
pub fn name(self, corpus_name: &str) -> DirCorpusBuilder<IS, HasName> {
DirCorpusBuilder {
items: self.items,
corpus_name: Some(corpus_name.to_string()),
_item_state: PhantomData,
_name_state: PhantomData,
}
}
}
impl<IS, NS> DirCorpusBuilder<IS, NS>
where
IS: CorpusBuildState,
NS: CorpusBuildState,
{
pub fn directory<P>(
mut self,
directory: P,
) -> Result<DirCorpusBuilder<HasItems, NS>, FeroxFuzzError>
where
P: AsRef<Path>,
{
let new_items = from_directory(directory)?;
self.items.extend(new_items);
Ok(DirCorpusBuilder {
items: self.items,
corpus_name: self.corpus_name,
_item_state: PhantomData,
_name_state: PhantomData,
})
}
}
impl DirCorpusBuilder<HasItems, HasName> {
pub fn build(self) -> CorpusType {
CorpusType::Dir(DirCorpus {
items: self.items,
corpus_name: self.corpus_name.unwrap(),
})
}
}