essential-app-utils 0.7.0

Utilities for building Essential applications
Documentation
use std::path::Path;

use essential_debugger::Source;
use essential_server_types::{QueryStateReads, StateReadRequestType};
use essential_types::solution::Solution;

use crate::compile::{get_contracts, NamedContract};

#[cfg(test)]
mod tests;

#[derive(Clone, Debug)]
pub struct Target {
    pub contract: String,
    pub predicate: String,
    pub data_index: usize,
    pub constraint: usize,
}

pub async fn debug(
    pint_directory: &Path,
    server_address: &str,
    solution: &Solution,
    target: Target,
) {
    let contracts = get_contracts(pint_directory.to_owned(), &[&target.contract])
        .await
        .unwrap();
    let predicate = contracts
        .get_contract(&target.contract)
        .unwrap()
        .get_predicate(&target.predicate)
        .unwrap()
        .clone();
    let query = QueryStateReads::from_solution(
        solution.clone(),
        target.data_index as u16,
        &predicate,
        StateReadRequestType::Reads,
    );

    let r = essential_rest_client::EssentialClient::new(server_address.to_string())
        .unwrap()
        .query_state_reads(query)
        .await
        .unwrap();
    let state = match r {
        essential_server_types::QueryStateReadsOutput::Reads(r) => r,
        _ => unreachable!(),
    };
    let state = state.into_iter().collect();
    let contract = contracts.get_contract(&target.contract).unwrap();
    let source = get_source(contract, &target.predicate, target.constraint);

    essential_debugger::run_with_source(
        solution.clone(),
        target.data_index as u16,
        predicate,
        target.constraint,
        state,
        source,
    )
    .await
    .unwrap();
}

impl Target {
    pub fn new(contract: &str, predicate: &str, data_index: usize, constraint: usize) -> Self {
        Self {
            contract: contract.to_string(),
            predicate: predicate.to_string(),
            data_index,
            constraint,
        }
    }

    pub async fn debug(self, pint_directory: &Path, server_address: &str, solution: &Solution) {
        debug(pint_directory, server_address, solution, self).await
    }
}

fn get_source(contract: &NamedContract, predicate_name: &str, constraint_num: usize) -> Source {
    let other: String = contract
        .source
        .lines()
        .take_while(|l| !l.starts_with("predicate "))
        .fold(String::new(), |acc, l| acc + l + "\n");
    let predicate_name = predicate_name
        .trim()
        .trim_start_matches("::")
        .to_lowercase();

    let mut count = 0;
    let predicate: String = contract
        .source
        .lines()
        .skip_while(|l| {
            if l.starts_with("predicate ") {
                let Some(name) = l.trim().split(' ').nth(1) else {
                    return true;
                };
                name.trim().trim_start_matches("::").to_lowercase() != predicate_name
            } else {
                true
            }
        })
        .take_while(|l| {
            if l.starts_with("predicate ") {
                count += 1;
            }
            count < 2
        })
        .fold(String::new(), |acc, l| acc + l + "\n");

    Source::default()
        .with_other_code(other)
        .with_predicate_find_line(predicate, constraint_num)
}