use crate::{
artifacts::{output_selection::OutputSelection, Settings},
resolver::GraphEdges,
Source, Sources,
};
use std::{
collections::BTreeMap,
fmt,
fmt::Formatter,
path::{Path, PathBuf},
};
pub trait FileFilter {
fn is_match(&self, file: &Path) -> bool;
}
impl<F> FileFilter for F
where
F: Fn(&Path) -> bool,
{
fn is_match(&self, file: &Path) -> bool {
(self)(file)
}
}
#[derive(Default)]
pub struct TestFileFilter {
_priv: (),
}
impl fmt::Debug for TestFileFilter {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("TestFileFilter").finish()
}
}
impl fmt::Display for TestFileFilter {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("TestFileFilter")
}
}
impl FileFilter for TestFileFilter {
fn is_match(&self, file: &Path) -> bool {
file.file_name().and_then(|s| s.to_str()).map(|s| s.ends_with(".t.sol")).unwrap_or_default()
}
}
pub enum SparseOutputFilter {
AllDirty,
Custom(Box<dyn FileFilter>),
}
impl SparseOutputFilter {
pub fn sparse_sources(
&self,
sources: FilteredSources,
settings: &mut Settings,
graph: &GraphEdges,
) -> Sources {
match self {
SparseOutputFilter::AllDirty => {
if !sources.all_dirty() {
Self::all_dirty(&sources, settings)
}
}
SparseOutputFilter::Custom(f) => {
Self::apply_custom_filter(&sources, settings, graph, f)
}
};
sources.into()
}
#[allow(clippy::borrowed_box)]
fn apply_custom_filter(
sources: &FilteredSources,
settings: &mut Settings,
graph: &GraphEdges,
f: &Box<dyn FileFilter>,
) {
tracing::trace!("optimizing output selection with custom filter",);
let selection = settings
.output_selection
.as_mut()
.remove("*")
.unwrap_or_else(OutputSelection::default_file_output_selection);
for (file, source) in sources.0.iter() {
let key = format!("{}", file.display());
if source.is_dirty() && f.is_match(file) {
settings.output_selection.as_mut().insert(key, selection.clone());
for link in graph.get_link_references(file) {
settings
.output_selection
.as_mut()
.insert(format!("{}", link.display()), selection.clone());
}
} else if !settings.output_selection.as_ref().contains_key(&key) {
tracing::trace!("using pruned output selection for {}", file.display());
settings
.output_selection
.as_mut()
.insert(key, OutputSelection::empty_file_output_select());
}
}
}
fn all_dirty(sources: &FilteredSources, settings: &mut Settings) {
tracing::trace!(
"optimizing output selection for {}/{} sources",
sources.clean().count(),
sources.len()
);
let selection = settings
.output_selection
.as_mut()
.remove("*")
.unwrap_or_else(OutputSelection::default_file_output_selection);
for (file, source) in sources.0.iter() {
if source.is_dirty() {
settings
.output_selection
.as_mut()
.insert(format!("{}", file.display()), selection.clone());
} else {
tracing::trace!("using pruned output selection for {}", file.display());
settings.output_selection.as_mut().insert(
format!("{}", file.display()),
OutputSelection::empty_file_output_select(),
);
}
}
}
}
impl From<Box<dyn FileFilter>> for SparseOutputFilter {
fn from(f: Box<dyn FileFilter>) -> Self {
SparseOutputFilter::Custom(f)
}
}
impl Default for SparseOutputFilter {
fn default() -> Self {
SparseOutputFilter::AllDirty
}
}
impl fmt::Debug for SparseOutputFilter {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
SparseOutputFilter::AllDirty => f.write_str("AllDirty"),
SparseOutputFilter::Custom(_) => f.write_str("Custom"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FilteredSources(pub BTreeMap<PathBuf, FilteredSource>);
impl FilteredSources {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn all_dirty(&self) -> bool {
self.0.values().all(|s| s.is_dirty())
}
pub fn dirty(&self) -> impl Iterator<Item = (&PathBuf, &FilteredSource)> + '_ {
self.0.iter().filter(|(_, s)| s.is_dirty())
}
pub fn clean(&self) -> impl Iterator<Item = (&PathBuf, &FilteredSource)> + '_ {
self.0.iter().filter(|(_, s)| !s.is_dirty())
}
pub fn dirty_files(&self) -> impl Iterator<Item = &PathBuf> + fmt::Debug + '_ {
self.0.iter().filter_map(|(k, s)| s.is_dirty().then_some(k))
}
}
impl From<FilteredSources> for Sources {
fn from(sources: FilteredSources) -> Self {
sources.0.into_iter().map(|(k, v)| (k, v.into_source())).collect()
}
}
impl From<Sources> for FilteredSources {
fn from(s: Sources) -> Self {
FilteredSources(s.into_iter().map(|(key, val)| (key, FilteredSource::Dirty(val))).collect())
}
}
impl From<BTreeMap<PathBuf, FilteredSource>> for FilteredSources {
fn from(s: BTreeMap<PathBuf, FilteredSource>) -> Self {
FilteredSources(s)
}
}
impl AsRef<BTreeMap<PathBuf, FilteredSource>> for FilteredSources {
fn as_ref(&self) -> &BTreeMap<PathBuf, FilteredSource> {
&self.0
}
}
impl AsMut<BTreeMap<PathBuf, FilteredSource>> for FilteredSources {
fn as_mut(&mut self) -> &mut BTreeMap<PathBuf, FilteredSource> {
&mut self.0
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum FilteredSource {
Dirty(Source),
Clean(Source),
}
impl FilteredSource {
pub fn source(&self) -> &Source {
match self {
FilteredSource::Dirty(s) => s,
FilteredSource::Clean(s) => s,
}
}
pub fn into_source(self) -> Source {
match self {
FilteredSource::Dirty(s) => s,
FilteredSource::Clean(s) => s,
}
}
pub fn is_dirty(&self) -> bool {
matches!(self, FilteredSource::Dirty(_))
}
}
#[derive(Debug)]
pub struct FilteredSourceInfo {
pub file: PathBuf,
pub source: Source,
pub idx: usize,
pub dirty: bool,
}