strop-engine 0.32.1

strop editor engine: documents, grammar, services and sessions — no frontend UI dependencies
use super::*;
use std::fmt::Write;
struct View {
    text: String,
    rows: Vec<ReviewRow>,
}
impl View {
    fn new() -> Self {
        Self {
            text: String::new(),
            rows: Vec::new(),
        }
    }
    fn line(&mut self, text: &str, row: ReviewRow) {
        self.text.push_str(text);
        self.text.push('\n');
        self.rows.push(row);
    }
    fn finish(self) -> (String, Vec<ReviewRow>) {
        (self.text, self.rows)
    }
}
fn description(operation: &OperationIntent) -> String {
    let source = operation.source.as_ref().map(ResourceLocation::label);
    let destination = operation.destination.as_ref().map(ResourceLocation::label);
    match (source, destination) {
        (Some(source), Some(destination)) => {
            format!("{}: {source}{destination}", operation.kind.label())
        }
        (Some(source), None) => format!("{}: {source}", operation.kind.label()),
        (None, Some(destination)) => format!("{}: {destination}", operation.kind.label()),
        _ => format!("{}: missing resource", operation.kind.label()),
    }
}
fn inventory(view: &mut View, batch: &strop_fs::batch::PreparedBatch) {
    for (index, step) in batch.steps.iter().enumerate() {
        view.line(
            &format!("{}. {}", index + 1, description(&step.intent)),
            if step.intent.kind.destructive() {
                ReviewRow::Removed
            } else {
                ReviewRow::Added
            },
        );
        if step.intent.kind == OperationKind::Copy {
            view.line(
                match step.intent.copy_version {
                    CopyVersion::Stored => "   copy stored bytes",
                    CopyVersion::Buffer => {
                        "   copy pinned current-buffer contents; original remains unsaved"
                    }
                },
                ReviewRow::Context,
            );
        }
        if !step.dependencies.is_empty() {
            let mut dependencies = String::from("   requires committed steps:");
            for dependency in &step.dependencies {
                let _ = write!(dependencies, " {}", dependency + 1);
            }
            view.line(&dependencies, ReviewRow::Context);
        }
    }
    for refusal in &batch.refused {
        view.line(
            &format!(
                "REFUSED {}{}",
                description(&refusal.intent),
                refusal.failure
            ),
            ReviewRow::Warning,
        );
    }
    let mut policies = std::collections::BTreeSet::new();
    for step in &batch.steps {
        for policy in [
            &step.capability.metadata_policy,
            &step.capability.concurrency_policy,
        ] {
            if policies.insert(policy) {
                view.line(policy, ReviewRow::Context);
            }
        }
    }
}
pub(super) fn proposal(
    id: WorkerId,
    batch: &strop_fs::batch::PreparedBatch,
) -> (String, Vec<ReviewRow>) {
    let mut view = View::new();
    view.line(
        &format!("filesystem change review {}", id.get()),
        ReviewRow::Heading,
    );
    view.line(
        &format!(
            "{} exact steps; {} refused; nothing applied",
            batch.steps.len(),
            batch.refused.len()
        ),
        ReviewRow::Context,
    );
    view.line(
        "Apply filesystem changes: :apply-change   Cancel: :cancel-change",
        ReviewRow::Heading,
    );
    if batch
        .steps
        .iter()
        .any(|step| step.intent.kind == OperationKind::Remove)
    {
        view.line(
            "PERMANENT DELETION: these Remove steps have no Trash recovery.",
            ReviewRow::Warning,
        );
    }
    view.line(
        "Missing parents appear as separate creation steps. Apply approves every listed step.",
        ReviewRow::Context,
    );
    inventory(&mut view, batch);
    view.finish()
}
pub(super) fn receipt(
    id: WorkerId,
    batch: &strop_fs::batch::PreparedBatch,
    receipts: &[StepReceipt],
    warning: Option<&str>,
) -> (String, Vec<ReviewRow>) {
    let mut view = View::new();
    view.line(
        &format!("filesystem operation {} — receipt", id.get()),
        ReviewRow::Heading,
    );
    view.line(
        "A batch is not atomic. Verify never retries a mutation.",
        ReviewRow::Context,
    );
    if let Some(warning) = warning {
        view.line(
            &format!("Worker cleanup warning: {warning}"),
            ReviewRow::Warning,
        );
    }
    for receipt in receipts {
        view.line(
            &format!(
                "{}. {}",
                receipt.step + 1,
                description(&receipt.operation.intent)
            ),
            ReviewRow::File,
        );
        match &receipt.outcome {
            StepOutcome::Committed {
                recovery, warnings, ..
            } => {
                view.line("   COMMITTED", ReviewRow::Added);
                if receipt.operation.intent.kind != OperationKind::Remove {
                    view.line(
                        &format!(
                            "   checked recovery: :fs undo {} {}",
                            id.get(),
                            receipt.step + 1
                        ),
                        ReviewRow::Context,
                    );
                }
                if let Some(recovery) = recovery {
                    view.line(
                        &format!("   recovery: {}", recovery.label()),
                        ReviewRow::Context,
                    );
                }
                for warning in warnings {
                    view.line(&format!("   warning: {warning}"), ReviewRow::Warning);
                }
            }
            StepOutcome::Refused(error) => {
                view.line(&format!("   REFUSED: {error}"), ReviewRow::Warning)
            }
            StepOutcome::Cancelled { detail } => {
                view.line(&format!("   CANCELLED: {detail}"), ReviewRow::Context)
            }
            StepOutcome::Unconfirmed {
                detail, recovery, ..
            } => {
                view.line(&format!("   UNCONFIRMED: {detail}"), ReviewRow::Warning);
                view.line(
                    &format!("   :fs verify {} {}", id.get(), receipt.step + 1),
                    ReviewRow::Context,
                );
                if let Some(recovery) = recovery {
                    view.line(
                        &format!("   possible recovery: {}", recovery.label()),
                        ReviewRow::Warning,
                    );
                }
            }
        }
    }
    for refusal in &batch.refused {
        view.line(
            &format!(
                "REFUSED {}{}",
                description(&refusal.intent),
                refusal.failure
            ),
            ReviewRow::Warning,
        );
    }
    view.finish()
}