knave-expand 0.14.21

The reading, scoping and expansion stages of knave
Documentation
use std::rc::Rc;

use rustc_hash::FxHashMap;

use super::structural_path_claim::StructuralPathClaim;

#[derive(Default, serde::Serialize, serde::Deserialize)]
pub(super) struct StructuralPathClaims {
    claims: FxHashMap<Rc<str>, StructuralPathClaim>,
}

impl StructuralPathClaims {
    pub(super) fn claim(
        &mut self,
        path: &str,
        claim: StructuralPathClaim,
    ) -> Result<(), StructuralPathClaim> {
        match self.claims.get(path) {
            Some(first) => Err(*first),
            None => {
                self.claims.insert(path.into(), claim);
                Ok(())
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{StructuralPathClaim, StructuralPathClaims};
    use crate::core::{BindingArena, BindingId, BindingInfo, BindingKind};
    use crate::source::{SourceId, Span};

    struct ClaimsFixture {
        traits: BindingArena,
        claims: StructuralPathClaims,
    }

    impl ClaimsFixture {
        const PATH: &str = "std::fmt::Display";

        fn new() -> Self {
            Self {
                traits: BindingArena::default(),
                claims: StructuralPathClaims::default(),
            }
        }

        fn declare(&mut self, name: &str) -> BindingId {
            self.traits
                .add(BindingInfo::new(name.into(), BindingKind::Local, None))
        }

        fn claim(&mut self, path: &str, owner: BindingId) -> Result<(), BindingId> {
            let clause = Span::new(SourceId::new(0), 0..1);
            self.claims
                .claim(path, StructuralPathClaim::new(owner, clause))
                .map_err(StructuralPathClaim::owner)
        }
    }

    #[test]
    fn the_first_trait_to_claim_a_path_holds_it() {
        let mut fixture = ClaimsFixture::new();
        let display = fixture.declare("display");
        assert_eq!(fixture.claim(ClaimsFixture::PATH, display), Ok(()));
    }

    #[test]
    fn a_second_claim_of_a_path_answers_with_the_first() {
        let mut fixture = ClaimsFixture::new();
        let display = fixture.declare("display");
        let shown = fixture.declare("shown");
        let _ = fixture.claim(ClaimsFixture::PATH, display);
        assert_eq!(fixture.claim(ClaimsFixture::PATH, shown), Err(display));
    }

    #[test]
    fn claims_of_different_paths_do_not_meet() {
        let mut fixture = ClaimsFixture::new();
        let display = fixture.declare("display");
        let hash = fixture.declare("hash");
        let _ = fixture.claim(ClaimsFixture::PATH, display);
        assert_eq!(fixture.claim("std::hash::Hash", hash), Ok(()));
    }
}