use super::super::{
PendingPluginWorkspaceIoBatch, PluginBufferHandle, PluginHandleStore, PluginHostContext,
PluginViewHandle, PluginWorkspaceIoBudget, PluginWorkspaceObserveTaskHandle,
PluginWorkspaceObserveTaskQueue,
effect::{PendingPluginUpdateBatch, PluginBufferEditProposal, PluginEffectBatchLimit},
workspace_io::PendingPluginWorkspaceObserveTaskBatch,
};
#[cfg(any(test, feature = "plugin-runtime"))]
use super::PluginBufferObservationError;
use super::snapshot::PluginHostImportSnapshot;
use super::{
PluginHostImportBatches, PluginHostImportDiscardReport, PluginHostImportError,
PluginWorkspaceObserveTaskResourceAuthority,
};
use crate::{
buffer::BufferEdit,
fs_utils::FilesystemConfig,
plugin::{
PluginCapabilityRef, PluginIdentity, PluginWorkspaceObserveOutcome, WorkspacePath,
WorkspacePathRef,
},
};
use std::fmt::{Debug, Formatter};
#[cfg(any(test, feature = "plugin-runtime"))]
use std::num::NonZeroU64;
pub struct PluginHostImportSession {
snapshot: PluginHostImportSnapshot,
effects: PendingPluginUpdateBatch,
workspace_io: PendingPluginWorkspaceIoBatch,
workspace_observe_tasks: PendingPluginWorkspaceObserveTaskBatch,
}
impl PluginHostImportSession {
pub fn new(
host: &PluginHostContext,
handles: &PluginHandleStore,
effect_limit: PluginEffectBatchLimit,
workspace_budget: PluginWorkspaceIoBudget,
) -> Result<Self, PluginHostImportError> {
Ok(Self::from_snapshot(
PluginHostImportSnapshot::new(host, handles)?,
effect_limit,
workspace_budget,
))
}
pub(in crate::plugin) fn from_snapshot(
snapshot: PluginHostImportSnapshot,
effect_limit: PluginEffectBatchLimit,
workspace_budget: PluginWorkspaceIoBudget,
) -> Self {
Self {
effects: PendingPluginUpdateBatch::from_validated_limit(
snapshot.identity_proof().clone(),
effect_limit,
),
workspace_io: PendingPluginWorkspaceIoBatch::from_validated_budget(
snapshot.identity_proof().clone(),
workspace_budget,
),
workspace_observe_tasks: PendingPluginWorkspaceObserveTaskBatch::new(
snapshot.identity_proof().clone(),
),
snapshot,
}
}
#[must_use]
pub(in crate::plugin) const fn identity_proof(&self) -> &PluginIdentity {
self.snapshot.identity_proof()
}
pub fn push_status_text(
&mut self,
view: PluginViewHandle,
text: impl AsRef<str>,
) -> Result<(), PluginHostImportError> {
self.snapshot
.authorize(PluginCapabilityRef::StatusPublish)?;
let view = self.snapshot.handles().resolve_view(view)?;
self.effects.push_status_text(view.target(), text)?;
Ok(())
}
#[cfg(any(test, feature = "plugin-runtime"))]
pub(in crate::plugin) fn observe_buffer(
&self,
buffer: PluginBufferHandle,
max_text_bytes: NonZeroU64,
) -> Result<super::super::handles::PluginBufferSnapshot, PluginHostImportError> {
self.snapshot
.authorize(PluginCapabilityRef::BufferObserve)?;
let resolved = self.snapshot.handles().resolve_buffer(buffer)?;
let snapshot = resolved.observed_snapshot().ok_or_else(|| {
PluginBufferObservationError::MissingSnapshot {
handle: buffer.shape(),
}
})?;
let byte_len = u64::try_from(snapshot.byte_len()).map_err(|_source| {
PluginBufferObservationError::TextTooLarge {
handle: buffer.shape(),
byte_len: u64::MAX,
max: max_text_bytes.get(),
}
})?;
if byte_len > max_text_bytes.get() {
return Err(PluginBufferObservationError::TextTooLarge {
handle: buffer.shape(),
byte_len,
max: max_text_bytes.get(),
}
.into());
}
Ok(snapshot.clone())
}
pub fn propose_buffer_edit(
&mut self,
buffer: PluginBufferHandle,
edit: BufferEdit,
) -> Result<(), PluginHostImportError> {
self.snapshot
.authorize(PluginCapabilityRef::BufferProposeEdit)?;
let resolved = self.snapshot.handles().resolve_buffer(buffer)?;
let base_revision = resolved.observed_revision().ok_or_else(|| {
PluginHostImportError::MissingObservedRevision {
handle: buffer.shape(),
}
})?;
self.effects
.propose_buffer_edit(PluginBufferEditProposal::from_observed_revision(
resolved.target(),
edit,
base_revision,
))?;
Ok(())
}
pub fn queue_workspace_observe(
&mut self,
path: WorkspacePathRef<'_>,
) -> Result<(), PluginHostImportError> {
let access = self.snapshot.authorize_workspace_read(path)?;
self.workspace_io.push_read(access)?;
Ok(())
}
#[allow(dead_code)]
pub(in crate::plugin) fn observe_workspace(
&mut self,
path: WorkspacePathRef<'_>,
filesystem: &FilesystemConfig,
) -> Result<PluginWorkspaceObserveOutcome, PluginHostImportError> {
let access = self.snapshot.authorize_workspace_read(path)?;
Ok(self.workspace_io.observe_existing(access, filesystem)?)
}
#[allow(dead_code)]
pub(in crate::plugin) fn queue_workspace_observe_task(
&mut self,
path: WorkspacePathRef<'_>,
queue: &mut PluginWorkspaceObserveTaskQueue,
) -> Result<PluginWorkspaceObserveTaskResourceAuthority, PluginHostImportError> {
let access = self.snapshot.authorize_workspace_read(path)?;
let handle = self
.workspace_observe_tasks
.reserve_charged(access, &mut self.workspace_io, queue)
.map_err(PluginHostImportError::from)?;
Ok(PluginWorkspaceObserveTaskResourceAuthority::new(
handle,
WorkspacePath::from_ref(path),
))
}
#[must_use]
pub(in crate::plugin) fn has_pending_workspace_observe_task(
&self,
handle: &PluginWorkspaceObserveTaskHandle,
) -> bool {
self.workspace_observe_tasks.contains_handle(handle)
}
pub(in crate::plugin) fn remove_pending_workspace_observe_task(
&mut self,
handle: &PluginWorkspaceObserveTaskHandle,
) -> bool {
self.workspace_observe_tasks.remove_handle(handle)
}
pub(in crate::plugin) fn cancel_pending_workspace_observe_tasks(
&mut self,
identity: &PluginIdentity,
) -> usize {
self.workspace_observe_tasks.cancel_identity(identity)
}
pub(in crate::plugin) fn authorize_workspace_observe_task_resource(
&self,
path: WorkspacePathRef<'_>,
) -> Result<(), PluginHostImportError> {
self.snapshot
.authorize(PluginCapabilityRef::WorkspaceObserve(path))?;
Ok(())
}
pub fn queue_workspace_artifact_write(
&mut self,
path: WorkspacePathRef<'_>,
bytes: impl Into<Vec<u8>>,
) -> Result<(), PluginHostImportError> {
let access = self.snapshot.authorize_workspace_write(path)?;
self.workspace_io.push_write(access, bytes)?;
Ok(())
}
#[must_use]
pub fn seal(self) -> PluginHostImportBatches {
PluginHostImportBatches::new(
self.effects.seal(),
self.workspace_io.seal(),
self.workspace_observe_tasks.seal(),
)
}
#[must_use]
pub fn discard(self) -> PluginHostImportDiscardReport {
PluginHostImportDiscardReport::new(
self.effects.discard(),
self.workspace_io.discard(),
self.workspace_observe_tasks.discard(),
)
}
}
impl Debug for PluginHostImportSession {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("PluginHostImportSession")
.field("snapshot", &self.snapshot)
.field("effects", &self.effects)
.field("workspace_io", &self.workspace_io)
.field("workspace_observe_tasks", &self.workspace_observe_tasks)
.finish()
}
}