cco 0.2.0

cascading configuration
Documentation
//! [Item] is used to track state in [Context]
use crate::eval::index::Index;
use crate::expression::Expression;
use std::ops::Range;
use std::sync::Arc;

pub struct Item {
    /// The expression
    pub expr: Arc<Expression>,

    /// The resolution state of this item
    pub resolution: Resolution,

    /// The expression this was cloned from
    pub cloned_from: Option<Index>,

    /// An expression to consult when resolving variables
    pub context: Vec<Index>,

    /// Location in code
    pub span: Option<Range<usize>>,

    /// The source document this item comes from
    pub document: Option<std::sync::Arc<crate::Document>>,
}

impl Item {
    pub fn new(expr: impl Into<Expression>) -> Self {
        Item {
            expr: Arc::new(expr.into()),
            resolution: Default::default(),
            cloned_from: None,
            context: vec![],
            span: None,
            document: None,
        }
    }
    pub fn expr(&self) -> Arc<Expression> {
        self.expr.clone()
    }

    pub fn span(&self) -> Option<Range<usize>> {
        self.span.clone()
    }
}

impl std::fmt::Debug for Item {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use std::fmt::Write;

        match &self.resolution {
            Resolution::Unresolved => f.write_str("u:")?,
            Resolution::Value(value) => f.write_fmt(format_args!("v[{:?}]:", value.kind()))?,
            Resolution::Reference(referenced) => {
                f.write_fmt(format_args!("r[{referenced}]:"))?;
            }
        }

        self.expr.fmt(f)?;
        if self.span.is_some() {
            f.write_str("(")?;
            self.span.as_ref().unwrap().fmt(f)?;
            f.write_char(')')?;
        } else {
            f.write_char('?')?;
        }

        if let Some(cloned) = self.cloned_from {
            f.write_fmt(format_args!(" source[{cloned}]"))?;
        }

        f.write_str(" ctx")?;
        f.debug_list().entries(self.context.iter()).finish()
    }
}

impl<I: Into<Expression>, S: Into<Option<Range<usize>>>> From<(I, S)> for Item {
    fn from((expr, span): (I, S)) -> Self {
        Self {
            expr: Arc::new(expr.into()),
            resolution: Resolution::default(),
            cloned_from: None,
            context: vec![],
            span: span.into(),
            document: None,
        }
    }
}

#[derive(Debug, Default, Clone)]
pub enum Resolution {
    #[default]
    Unresolved,
    Value(crate::value::Value),
    Reference(Index),
}

impl Resolution {
    pub fn as_reference(&self) -> Option<Index> {
        match self {
            Resolution::Reference(reference) => Some(*reference),
            Resolution::Unresolved | Resolution::Value(_) => None,
        }
    }
    pub fn as_value(&self) -> Option<&crate::value::Value> {
        match self {
            Resolution::Value(value) => Some(value),
            Resolution::Reference(_) | Resolution::Unresolved => None,
        }
    }
}

impl From<Index> for Resolution {
    fn from(value: Index) -> Self {
        Self::Reference(value)
    }
}