remem-ai 0.6.39

Local-first coding agent memory for Claude Code and OpenAI Codex
Documentation
use brush_parser::ast::{
    AndOr, CaseClauseCommand, CaseItemPostAction, CompoundList, IfClauseCommand,
    WhileOrUntilClauseCommand,
};

use super::{CommandCollector, DYNAMIC_SHELL_WORD};

impl CommandCollector {
    pub(super) fn collect_list(&mut self, list: &CompoundList) -> Result<(), String> {
        for item in &list.0 {
            if self.execution_terminated {
                break;
            }
            self.collect_pipeline(&item.0.first)?;
            if self.execution_terminated {
                break;
            }
            let mut static_success = self.static_pipeline_success(&item.0.first)?;
            if self.positional_context.is_none() {
                self.collect_unscoped_and_or(&item.0.additional, &mut static_success)?;
                continue;
            }
            let (mut chain_success, mut chain_failure) =
                self.take_positional_outcomes(item.0.first.bang, static_success);
            for additional in &item.0.additional {
                if self.execution_terminated {
                    break;
                }
                let (execute_on_success, pipeline) = match additional {
                    AndOr::And(pipeline) => (true, pipeline),
                    AndOr::Or(pipeline) => (false, pipeline),
                };
                let (selected, skipped) = if execute_on_success {
                    (chain_success.take(), chain_failure.take())
                } else {
                    (chain_failure.take(), chain_success.take())
                };
                let definitely_executes = static_success == Some(execute_on_success);
                let (next_success, next_failure, pipeline_success) =
                    if let Some(selected) = selected {
                        self.positional_context = Some(selected);
                        self.with_positional_branch_execution(definitely_executes, |collector| {
                            collector.collect_pipeline(pipeline)
                        })?;
                        let pipeline_success = self.static_pipeline_success(pipeline)?;
                        let (success, failure) =
                            self.take_positional_outcomes(pipeline.bang, pipeline_success);
                        (success, failure, pipeline_success)
                    } else {
                        (None, None, None)
                    };
                if execute_on_success {
                    chain_success = next_success;
                    chain_failure =
                        super::shell_state::merge_positional_contexts(skipped, next_failure);
                    static_success = super::and_status(static_success, pipeline_success);
                } else {
                    chain_success =
                        super::shell_state::merge_positional_contexts(skipped, next_success);
                    chain_failure = next_failure;
                    static_success = super::or_status(static_success, pipeline_success);
                }
                self.positional_context = super::shell_state::merge_positional_contexts(
                    chain_success.clone(),
                    chain_failure.clone(),
                );
            }
            self.last_positional_status = match (chain_success.is_some(), chain_failure.is_some()) {
                (true, false) => Some(true),
                (false, true) => Some(false),
                _ => None,
            };
            self.last_positional_success = chain_success;
            self.last_positional_failure = chain_failure;
        }
        Ok(())
    }

    fn collect_unscoped_and_or(
        &mut self,
        additional: &[AndOr],
        static_success: &mut Option<bool>,
    ) -> Result<(), String> {
        for additional in additional {
            if self.execution_terminated {
                break;
            }
            match additional {
                AndOr::And(_) if *static_success == Some(false) => {}
                AndOr::Or(_) if *static_success == Some(true) => {}
                AndOr::And(pipeline) => {
                    let definitely_executes = *static_success == Some(true);
                    self.with_execution_certainty(definitely_executes, |collector| {
                        collector.collect_pipeline(pipeline)
                    })?;
                    *static_success =
                        super::and_status(*static_success, self.static_pipeline_success(pipeline)?);
                }
                AndOr::Or(pipeline) => {
                    let definitely_executes = *static_success == Some(false);
                    self.with_execution_certainty(definitely_executes, |collector| {
                        collector.collect_pipeline(pipeline)
                    })?;
                    *static_success =
                        super::or_status(*static_success, self.static_pipeline_success(pipeline)?);
                }
            }
        }
        Ok(())
    }

    pub(super) fn collect_case_clause(
        &mut self,
        command: &CaseClauseCommand,
    ) -> Result<(), String> {
        self.collect_word_commands(&command.value)?;
        let value = self.command_word(&command.value)?;
        let mut force_next_definite = false;
        let mut force_next_possible = false;
        let mut prior_possible_match = false;
        for case in &command.cases {
            for pattern in &case.patterns {
                self.collect_word_commands(pattern)?;
            }
            let mut match_state = if force_next_definite {
                Some(true)
            } else if force_next_possible {
                None
            } else {
                self.static_case_match(&value, &case.patterns)?
            };
            if prior_possible_match && match_state == Some(true) {
                match_state = None;
            }
            force_next_definite = false;
            force_next_possible = false;
            if match_state != Some(false) {
                if let Some(commands) = &case.cmd {
                    self.with_execution_certainty(
                        match_state == Some(true) && !prior_possible_match,
                        |collector| collector.collect_list(commands),
                    )?;
                }
                match case.post_action {
                    CaseItemPostAction::ExitCase if match_state == Some(true) => break,
                    CaseItemPostAction::UnconditionallyExecuteNextCaseItem => {
                        force_next_definite = match_state == Some(true);
                        force_next_possible = match_state.is_none();
                    }
                    CaseItemPostAction::ContinueEvaluatingCases | CaseItemPostAction::ExitCase => {}
                }
            }
            prior_possible_match |= match_state.is_none();
        }
        Ok(())
    }

    fn static_case_match(
        &self,
        value: &str,
        patterns: &[brush_parser::ast::Word],
    ) -> Result<Option<bool>, String> {
        if value == DYNAMIC_SHELL_WORD {
            return Ok(None);
        }
        let mut all_exact = true;
        for pattern in patterns {
            let pattern = self.command_word(pattern)?;
            if pattern == DYNAMIC_SHELL_WORD
                || pattern
                    .chars()
                    .any(|ch| matches!(ch, '*' | '?' | '[' | ']' | '(' | ')' | '|' | '\\'))
            {
                all_exact = false;
            } else if pattern == value || self.nocasematch && pattern.eq_ignore_ascii_case(value) {
                return Ok(Some(true));
            }
        }
        Ok(all_exact.then_some(false))
    }

    pub(super) fn collect_loop(
        &mut self,
        command: &WhileOrUntilClauseCommand,
        until: bool,
    ) -> Result<(), String> {
        self.collect_list(&command.0)?;
        let status = self.static_list_success(&command.0)?;
        let body_executes = status.map(|success| if until { !success } else { success });
        if body_executes != Some(false) {
            self.with_execution_certainty(body_executes == Some(true), |collector| {
                collector.collect_list(&command.1.list)
            })?;
        }
        Ok(())
    }

    pub(super) fn collect_if_clause(&mut self, command: &IfClauseCommand) -> Result<(), String> {
        self.collect_list(&command.condition)?;
        let status = self.static_list_success(&command.condition)?;
        if status != Some(false) {
            self.with_execution_certainty(status == Some(true), |collector| {
                collector.collect_list(&command.then)
            })?;
        }
        if status == Some(true) {
            return Ok(());
        }
        let mut branch_is_definite = status == Some(false);
        for branch in command.elses.iter().flatten() {
            let branch_status = if let Some(condition) = &branch.condition {
                self.with_execution_certainty(branch_is_definite, |collector| {
                    collector.collect_list(condition)
                })?;
                self.static_list_success(condition)?
            } else {
                Some(true)
            };
            if branch_status != Some(false) {
                self.with_execution_certainty(
                    branch_is_definite && branch_status == Some(true),
                    |collector| collector.collect_list(&branch.body),
                )?;
            }
            match branch_status {
                Some(true) => break,
                Some(false) => {}
                None => branch_is_definite = false,
            }
        }
        Ok(())
    }

    pub(super) fn static_list_success(&self, list: &CompoundList) -> Result<Option<bool>, String> {
        let Some(item) = list.0.last() else {
            return Ok(None);
        };
        let mut status = self.static_pipeline_success(&item.0.first)?;
        for additional in &item.0.additional {
            status = match additional {
                AndOr::And(pipeline) => {
                    super::and_status(status, self.static_pipeline_success(pipeline)?)
                }
                AndOr::Or(pipeline) => {
                    super::or_status(status, self.static_pipeline_success(pipeline)?)
                }
            };
        }
        Ok(status)
    }
}