foundry_compilers/
filter.rsuse crate::{
    compilers::{multi::MultiCompilerParsedSource, CompilerSettings, ParsedSource},
    resolver::{parse::SolData, GraphEdges},
    Sources,
};
use foundry_compilers_artifacts::output_selection::OutputSelection;
use std::{
    collections::HashSet,
    fmt,
    path::{Path, PathBuf},
};
pub trait FileFilter: dyn_clone::DynClone + Send + Sync {
    fn is_match(&self, file: &Path) -> bool;
}
dyn_clone::clone_trait_object!(FileFilter);
impl<F: Fn(&Path) -> bool + Clone + Send + Sync> FileFilter for F {
    fn is_match(&self, file: &Path) -> bool {
        (self)(file)
    }
}
#[derive(Clone, Default)]
pub struct TestFileFilter {
    _priv: (),
}
impl fmt::Debug for TestFileFilter {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TestFileFilter").finish()
    }
}
impl fmt::Display for TestFileFilter {
    fn fmt(&self, f: &mut fmt::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 trait MaybeSolData {
    fn sol_data(&self) -> Option<&SolData>;
}
impl MaybeSolData for SolData {
    fn sol_data(&self) -> Option<&SolData> {
        Some(self)
    }
}
impl MaybeSolData for MultiCompilerParsedSource {
    fn sol_data(&self) -> Option<&SolData> {
        match self {
            Self::Solc(data) => Some(data),
            _ => None,
        }
    }
}
#[derive(Default)]
pub enum SparseOutputFilter<'a> {
    #[default]
    Optimized,
    Custom(&'a dyn FileFilter),
}
impl<'a> SparseOutputFilter<'a> {
    pub fn new(filter: Option<&'a dyn FileFilter>) -> Self {
        if let Some(f) = filter {
            SparseOutputFilter::Custom(f)
        } else {
            SparseOutputFilter::Optimized
        }
    }
    pub fn sparse_sources<D: ParsedSource, S: CompilerSettings>(
        &self,
        sources: &Sources,
        settings: &mut S,
        graph: &GraphEdges<D>,
    ) -> Vec<PathBuf> {
        let mut full_compilation: HashSet<PathBuf> = sources
            .dirty_files()
            .flat_map(|file| {
                if let Self::Custom(f) = self {
                    if !f.is_match(file) {
                        return vec![];
                    }
                }
                let mut required_sources = vec![file.clone()];
                if let Some(data) = graph.get_parsed_source(file) {
                    let imports = graph.imports(file).into_iter().filter_map(|import| {
                        graph.get_parsed_source(import).map(|data| (import.as_path(), data))
                    });
                    for import in data.compilation_dependencies(imports) {
                        let import = import.to_path_buf();
                        #[cfg(windows)]
                        let import = {
                            use path_slash::PathBufExt;
                            PathBuf::from(import.to_slash_lossy().to_string())
                        };
                        required_sources.push(import);
                    }
                }
                required_sources
            })
            .collect();
        full_compilation.retain(|file| sources.0.get(file).is_some_and(|s| s.is_dirty()));
        settings.update_output_selection(|selection| {
            trace!(
                "optimizing output selection for {} sources",
                sources.len() - full_compilation.len()
            );
            let default_selection = selection
                .as_mut()
                .remove("*")
                .unwrap_or_else(OutputSelection::default_file_output_selection);
            for file in sources.0.keys() {
                let key = file.display().to_string();
                let output = if full_compilation.contains(file) {
                    default_selection.clone()
                } else {
                    OutputSelection::empty_file_output_select()
                };
                selection.as_mut().insert(key, output);
            }
        });
        full_compilation.into_iter().collect()
    }
}
impl fmt::Debug for SparseOutputFilter<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            SparseOutputFilter::Optimized => f.write_str("Optimized"),
            SparseOutputFilter::Custom(_) => f.write_str("Custom"),
        }
    }
}