knave-expand 0.13.2

The reading, scoping and expansion stages of knave
Documentation
use super::recorded_inputs::{BoundName, LoadedModule, RecordedInputs};
use super::{Expander, Program};
use crate::core::ModulePath;
use crate::diagnostic::Diagnostics;
use crate::source::{SourceId, Span};

pub struct ModuleSurvey {
    pub program: Result<Program, Diagnostics>,
    pub names: Vec<Vec<BoundName>>,
    pub loaded: Vec<LoadedModule>,
}

impl Expander<'_> {
    pub fn survey(
        mut self,
        path: ModulePath,
        sources: &[SourceId],
        inputs: &[Span],
    ) -> ModuleSurvey {
        self.recorded_inputs = RecordedInputs::new(inputs.to_vec());
        let entry = self.load_source_module(path, sources);
        let recorded = std::mem::take(&mut self.recorded_inputs);
        ModuleSurvey {
            names: inputs
                .iter()
                .map(|input| recorded.bound_by(*input))
                .collect(),
            loaded: recorded.loaded().to_vec(),
            program: self.into_program(entry),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    use super::*;
    use crate::modules::ModuleSources;
    use crate::source::SourceMap;

    struct SurveyFixture;

    impl SurveyFixture {
        fn survey(texts: &[&str]) -> ModuleSurvey {
            let mut sources = SourceMap::default();
            let ids: Vec<SourceId> = texts
                .iter()
                .map(|text| sources.add("prompt", *text))
                .collect();
            let inputs: Vec<Span> = ids
                .iter()
                .zip(texts)
                .map(|(id, text)| Span::new(*id, 0..text.len()))
                .collect();
            Expander::new(
                &mut sources,
                ModuleSources::new(PathBuf::new(), PathBuf::new()),
            )
            .survey(ModulePath::new("prompt"), &ids, &inputs)
        }

        fn names(survey: &ModuleSurvey) -> Vec<Vec<String>> {
            survey
                .names
                .iter()
                .map(|bound| bound.iter().map(|name| name.name.to_string()).collect())
                .collect()
        }
    }

    #[test]
    fn a_survey_names_each_input_even_when_the_module_has_an_error() {
        let survey = SurveyFixture::survey(&["(defn twice (x) (* 2 x))", "(defn twice (x) x)"]);
        assert!(survey.program.is_err());
        assert_eq!(SurveyFixture::names(&survey), [["twice"], ["twice"]]);
    }

    #[test]
    fn a_failed_definer_binds_no_name_of_its_input() {
        let survey = SurveyFixture::survey(&["(defn area \"doc\" (w) w)", "(area 1)"]);
        assert!(survey.program.is_err());
        assert_eq!(
            SurveyFixture::names(&survey),
            [Vec::<String>::new(), Vec::new()]
        );
    }
}