harn-kernel 0.10.53

Portable compiler, program artifact, and deterministic execution kernel for Harn
Documentation
use super::*;

fn runtime_text(value: &RuntimeValue) -> Option<String> {
    (!matches!(value, RuntimeValue::Nil)).then(|| value.display())
}

fn regex_capture_value(capture: crate::pure::RegexCapture) -> RuntimeValue {
    let mut values = BTreeMap::from([
        (
            "match".to_string(),
            RuntimeValue::String(Arc::from(capture.full_match)),
        ),
        (
            "groups".to_string(),
            RuntimeValue::List(Rc::new(
                capture
                    .groups
                    .into_iter()
                    .map(|value| {
                        value.map_or(RuntimeValue::Nil, |value| {
                            RuntimeValue::String(Arc::from(value))
                        })
                    })
                    .collect(),
            )),
        ),
        ("start".to_string(), RuntimeValue::Int(capture.start as i64)),
        ("end".to_string(), RuntimeValue::Int(capture.end as i64)),
        ("line".to_string(), RuntimeValue::Int(capture.line as i64)),
    ]);
    values.extend(
        capture
            .named
            .into_iter()
            .map(|(name, value)| (name, RuntimeValue::String(Arc::from(value)))),
    );
    RuntimeValue::Record(Rc::new(values))
}

fn is_filter_nil_value(value: &RuntimeValue) -> bool {
    matches!(value, RuntimeValue::Nil)
        || matches!(value, RuntimeValue::String(value) if value.is_empty() || value.as_ref() == "null")
}

impl Machine<'_> {
    pub(super) fn call_builtin(&mut self, name: &str, args: Vec<RuntimeValue>) -> OpStep {
        if let Err(diagnostic) = self.charge_call_validation(&args) {
            return OpStep::Error(diagnostic);
        }
        let Some(builtin) = PortableBuiltin::from_name(name) else {
            return OpStep::Error(diagnostic(
                "unsupported_builtin",
                format!("builtin `{name}` is not implemented by the portable kernel"),
            ));
        };
        match (builtin, args.as_slice()) {
            (PortableBuiltin::Len | PortableBuiltin::Count, [RuntimeValue::List(v)]) => {
                OpStep::Push(RuntimeValue::Int(v.len() as i64))
            }
            (PortableBuiltin::Len | PortableBuiltin::Count, [RuntimeValue::String(v)]) => {
                OpStep::Push(RuntimeValue::Int(v.chars().count() as i64))
            }
            (PortableBuiltin::String | PortableBuiltin::ToString, [value]) => {
                match self.render_value(value) {
                    Ok(value) => self.push_charged(RuntimeValue::String(Arc::from(value))),
                    Err(diagnostic) => OpStep::Error(diagnostic),
                }
            }
            (PortableBuiltin::HexEncode, [RuntimeValue::String(value)]) => self.push_charged(
                RuntimeValue::String(Arc::from(crate::pure::hex_encode(value.as_bytes()))),
            ),
            (PortableBuiltin::HexEncode, [RuntimeValue::Bytes(value)]) => self.push_charged(
                RuntimeValue::String(Arc::from(crate::pure::hex_encode(value))),
            ),
            (PortableBuiltin::HexDecode, [RuntimeValue::String(value)]) => {
                match crate::pure::hex_decode_text(value) {
                    Ok(value) => self.push_charged(RuntimeValue::String(Arc::from(value))),
                    Err(message) => OpStep::Error(diagnostic("builtin_error", message)),
                }
            }
            (PortableBuiltin::Trim, [value]) => match self.render_value(value) {
                Ok(value) => self.push_charged(RuntimeValue::String(Arc::from(
                    crate::pure::trim_text(&value),
                ))),
                Err(diagnostic) => OpStep::Error(diagnostic),
            },
            (PortableBuiltin::Replace, [input, old, new]) => {
                match (
                    self.render_value(input),
                    self.render_value(old),
                    self.render_value(new),
                ) {
                    (Ok(input), Ok(old), Ok(new)) => self.push_charged(RuntimeValue::String(
                        Arc::from(crate::pure::replace_text(&input, &old, &new)),
                    )),
                    (Err(diagnostic), _, _) | (_, Err(diagnostic), _) | (_, _, Err(diagnostic)) => {
                        OpStep::Error(diagnostic)
                    }
                }
            }
            (PortableBuiltin::StartsWith, [input, prefix]) => {
                match (self.render_value(input), self.render_value(prefix)) {
                    (Ok(input), Ok(prefix)) => OpStep::Push(RuntimeValue::Bool(
                        crate::pure::starts_with_text(&input, &prefix),
                    )),
                    (Err(diagnostic), _) | (_, Err(diagnostic)) => OpStep::Error(diagnostic),
                }
            }
            (PortableBuiltin::JsonStringify, [value]) => {
                match DataValue::try_from(value.clone()).and_then(|value| {
                    serde_json::to_string(&value.to_json()).map_err(|error| {
                        diagnostic("json_stringify", format!("json_stringify: {error}"))
                    })
                }) {
                    Ok(value) => self.push_charged(RuntimeValue::String(Arc::from(value))),
                    Err(diagnostic) => OpStep::Error(diagnostic),
                }
            }
            (PortableBuiltin::RegexMatch, [pattern, value])
            | (PortableBuiltin::RegexMatch, [pattern, value, _]) => {
                let (Some(pattern), Some(value)) = (runtime_text(pattern), runtime_text(value))
                else {
                    return OpStep::Push(RuntimeValue::Nil);
                };
                let flags = args.get(2).and_then(runtime_text).unwrap_or_default();
                match crate::pure::regex_matches(&pattern, &value, &flags) {
                    Ok(values) if values.is_empty() => OpStep::Push(RuntimeValue::Nil),
                    Ok(values) => self.push_charged(RuntimeValue::List(Rc::new(
                        values
                            .into_iter()
                            .map(|value| RuntimeValue::String(Arc::from(value)))
                            .collect(),
                    ))),
                    Err(message) => OpStep::Error(diagnostic(
                        "invalid_regex",
                        format!("Invalid regex: {message}"),
                    )),
                }
            }
            (PortableBuiltin::RegexReplace, [pattern, replacement, value])
            | (PortableBuiltin::RegexReplace, [pattern, replacement, value, _]) => {
                let (Some(pattern), Some(replacement), Some(value)) = (
                    runtime_text(pattern),
                    runtime_text(replacement),
                    runtime_text(value),
                ) else {
                    return OpStep::Push(RuntimeValue::Nil);
                };
                let flags = args.get(3).and_then(runtime_text).unwrap_or_default();
                match crate::pure::regex_replace(&pattern, &replacement, &value, &flags) {
                    Ok(value) => self.push_charged(RuntimeValue::String(Arc::from(value))),
                    Err(message) => OpStep::Error(diagnostic(
                        "invalid_regex",
                        format!("Invalid regex: {message}"),
                    )),
                }
            }
            (PortableBuiltin::RegexCaptures, [pattern, value])
            | (PortableBuiltin::RegexCaptures, [pattern, value, _]) => {
                let (Some(pattern), Some(value)) = (runtime_text(pattern), runtime_text(value))
                else {
                    return OpStep::Push(RuntimeValue::List(Rc::new(Vec::new())));
                };
                let flags = args.get(2).and_then(runtime_text).unwrap_or_default();
                match crate::pure::regex_captures(&pattern, &value, &flags) {
                    Ok(captures) => self.push_charged(RuntimeValue::List(Rc::new(
                        captures.into_iter().map(regex_capture_value).collect(),
                    ))),
                    Err(message) => OpStep::Error(diagnostic(
                        "invalid_regex",
                        format!("Invalid regex: {message}"),
                    )),
                }
            }
            (PortableBuiltin::RegexSplit, [value, pattern])
            | (PortableBuiltin::RegexSplit, [value, pattern, _]) => {
                let (Some(value), Some(pattern)) = (runtime_text(value), runtime_text(pattern))
                else {
                    return OpStep::Push(RuntimeValue::Nil);
                };
                let flags = args.get(2).and_then(runtime_text).unwrap_or_default();
                match crate::pure::regex_split(&pattern, &value, &flags) {
                    Ok(values) => self.push_charged(RuntimeValue::List(Rc::new(
                        values
                            .into_iter()
                            .map(|value| RuntimeValue::String(Arc::from(value)))
                            .collect(),
                    ))),
                    Err(message) => OpStep::Error(diagnostic(
                        "invalid_regex",
                        format!("Invalid regex: {message}"),
                    )),
                }
            }
            (PortableBuiltin::Sha256, [RuntimeValue::String(value)]) => self.push_charged(
                RuntimeValue::String(Arc::from(crate::pure::sha256_hex(value.as_bytes()))),
            ),
            (PortableBuiltin::Sha256, [RuntimeValue::Bytes(value)]) => self.push_charged(
                RuntimeValue::String(Arc::from(crate::pure::sha256_hex(value))),
            ),
            (PortableBuiltin::SecretScan, [value]) => {
                let Some(content) = runtime_text(value) else {
                    return OpStep::Error(diagnostic(
                        "builtin_error",
                        "secret_scan: content is required",
                    ));
                };
                match serde_json::to_value(crate::pure::scan_secrets(&content))
                    .map_err(|error| diagnostic("builtin_error", format!("secret_scan: {error}")))
                    .and_then(DataValue::from_json)
                {
                    Ok(value) => self.push_charged(RuntimeValue::from(value)),
                    Err(diagnostic) => OpStep::Error(diagnostic),
                }
            }
            (PortableBuiltin::PathJoin, values) => {
                let mut segments = Vec::with_capacity(values.len());
                for value in values {
                    match self.render_value(value) {
                        Ok(value) => segments.push(value),
                        Err(diagnostic) => return OpStep::Error(diagnostic),
                    }
                }
                self.push_charged(RuntimeValue::String(Arc::from(
                    crate::pure::join_path_segments(&segments),
                )))
            }
            (PortableBuiltin::DictFilterNil, [RuntimeValue::Record(values)]) => {
                let filtered = values
                    .iter()
                    .filter(|(_, value)| !is_filter_nil_value(value))
                    .map(|(key, value)| (key.clone(), value.clone()))
                    .collect();
                self.push_charged(RuntimeValue::Record(Rc::new(filtered)))
            }
            (
                PortableBuiltin::MakeStruct,
                [RuntimeValue::String(_), RuntimeValue::Record(values), _],
            ) => OpStep::Push(RuntimeValue::Record(values.clone())),
            (PortableBuiltin::AssertList, [RuntimeValue::List(_)]) => {
                OpStep::Push(RuntimeValue::Nil)
            }
            (PortableBuiltin::AssertList, [value]) => OpStep::Error(diagnostic(
                "list_type",
                format!(
                    "cannot destructure {} with [...] pattern — expected list",
                    runtime_value_kind(value)
                ),
            )),
            (PortableBuiltin::AssertSchema, [value, RuntimeValue::String(name), schema]) => {
                match DataValue::try_from(schema.clone()) {
                    Ok(schema) if matches_compiler_schema(value, &schema) => {
                        OpStep::Push(RuntimeValue::Nil)
                    }
                    Ok(_) => OpStep::Error(diagnostic(
                        "argument_type",
                        format!("parameter `{name}` rejected {}", runtime_value_kind(value)),
                    )),
                    Err(diagnostic) => OpStep::Error(diagnostic),
                }
            }
            _ => OpStep::Error(diagnostic(
                "unsupported_builtin",
                format!("builtin `{name}` is not implemented by the portable kernel"),
            )),
        }
    }
}