hara-native 0.1.13

HAL-free native host runtime and package launcher for Hara
Documentation
use crate::lang::data::MetadataValue;
use crate::Runtime;

#[derive(Clone, Debug, PartialEq)]
pub enum DocumentationValue {
    Nil,
    Boolean(bool),
    Integer(i64),
    String(String),
    Array(Vec<DocumentationValue>),
}

#[derive(Clone, Debug, PartialEq)]
pub struct Documentation {
    pub symbol: String,
    pub doc: Option<String>,
    pub arglists: DocumentationValue,
    pub file: Option<String>,
    pub line: Option<i64>,
    pub column: Option<i64>,
}

fn value(metadata: &MetadataValue) -> DocumentationValue {
    match metadata {
        MetadataValue::Nil => DocumentationValue::Nil,
        MetadataValue::Boolean(value) => DocumentationValue::Boolean(*value),
        MetadataValue::Number(value) => DocumentationValue::Integer(*value),
        MetadataValue::Float(value) => DocumentationValue::String(value.to_string()),
        MetadataValue::BigInteger(value) => DocumentationValue::String(value.to_string()),
        MetadataValue::Regex(value) => DocumentationValue::String(value.clone()),
        MetadataValue::Character(value) => DocumentationValue::String(value.to_string()),
        MetadataValue::Tagged(tag, tagged) => {
            DocumentationValue::Array(vec![DocumentationValue::String(tag.clone()), value(tagged)])
        }
        MetadataValue::String(value) => DocumentationValue::String(value.clone()),
        MetadataValue::Keyword(value) => DocumentationValue::String(format!(":{}", value.as_str())),
        MetadataValue::Symbol(value) => DocumentationValue::String(value.as_str().to_owned()),
        MetadataValue::Vector(values)
        | MetadataValue::List(values)
        | MetadataValue::Set(values) => {
            DocumentationValue::Array(values.iter().map(value).collect())
        }
        MetadataValue::Map(values) => DocumentationValue::Array(
            values
                .iter()
                .flat_map(|(key, item)| [value(key), value(item)])
                .collect(),
        ),
    }
}

fn string(value: Option<&MetadataValue>) -> Option<String> {
    match value {
        Some(MetadataValue::String(value)) => Some(value.clone()),
        Some(MetadataValue::Symbol(value)) => Some(value.as_str().to_owned()),
        _ => None,
    }
}

fn integer(value: Option<&MetadataValue>) -> Option<i64> {
    match value {
        Some(MetadataValue::Number(value)) => Some(*value),
        Some(MetadataValue::String(value)) => value.parse().ok(),
        _ => None,
    }
}

pub(super) fn lookup(runtime: &Runtime, symbol: &str) -> Result<Documentation, String> {
    let metadata = runtime
        .var_metadata(symbol)
        .ok_or_else(|| format!("No documentation symbol: {symbol}"))?;
    let hara = metadata.hara.as_deref();
    let doc = hara
        .and_then(|value| value.doc().map(str::to_owned))
        .or(metadata.doc);
    let arglists = hara
        .and_then(|value| value.get_keyword("arglists"))
        .map(value)
        .unwrap_or_else(|| {
            DocumentationValue::Array(
                metadata
                    .arglists
                    .into_iter()
                    .map(DocumentationValue::String)
                    .collect(),
            )
        });
    Ok(Documentation {
        symbol: symbol.into(),
        doc,
        arglists,
        file: string(hara.and_then(|value| value.get_keyword("file"))),
        line: integer(hara.and_then(|value| value.get_keyword("line"))),
        column: integer(hara.and_then(|value| value.get_keyword("column"))),
    })
}