use super::resources::{
workspace_observe_task_resource_error, workspace_observe_task_resource_unavailable,
};
use crate::plugin::{
PluginHostImportError, PluginHostImportSession, PluginHostResourceKind, PluginIdentity,
PluginResourceHandleRejectionReason, PluginWorkspaceObserveOutcome,
PluginWorkspaceObserveOutcomeShape, PluginWorkspaceObserveTaskCancellationReport,
PluginWorkspaceObserveTaskEnqueueError, PluginWorkspaceObserveTaskEnqueueReport,
PluginWorkspaceObserveTaskHandle, PluginWorkspaceObserveTaskPoll,
PluginWorkspaceObserveTaskQueue, PluginWorkspaceObserveTaskQueueLimit,
PluginWorkspaceObserveTaskTake, SealedPluginWorkspaceObserveTaskBatch, WorkspacePath,
abi::bindings::alma::editor::host as wit_host,
host::PluginWorkspaceObserveTaskResourceAuthority,
};
use std::num::NonZeroUsize;
use wasmtime::component::{Resource, ResourceTable, ResourceTableError};
#[derive(Debug)]
pub(super) struct WasmtimeWorkspaceObserveTasks {
queue: PluginWorkspaceObserveTaskQueue,
resources: WasmtimeWorkspaceObserveTaskResources,
resources_started_in_update: WasmtimeStartedWorkspaceObserveTaskResources,
}
impl WasmtimeWorkspaceObserveTasks {
pub(super) fn new(limit: PluginWorkspaceObserveTaskQueueLimit) -> Self {
let queue = PluginWorkspaceObserveTaskQueue::from_validated_limit(limit);
let resources = WasmtimeWorkspaceObserveTaskResources::new(queue.limit());
Self {
queue,
resources,
resources_started_in_update: WasmtimeStartedWorkspaceObserveTaskResources::new(),
}
}
pub(super) const fn queue_mut(&mut self) -> &mut PluginWorkspaceObserveTaskQueue {
&mut self.queue
}
pub(super) fn enqueue(
&mut self,
batch: SealedPluginWorkspaceObserveTaskBatch,
) -> Result<PluginWorkspaceObserveTaskEnqueueReport, PluginWorkspaceObserveTaskEnqueueError>
{
batch.enqueue(&mut self.queue)
}
pub(super) fn execute_all(
&mut self,
filesystem: &crate::fs_utils::FilesystemConfig,
) -> Vec<crate::plugin::PluginWorkspaceObserveTaskCompletion> {
self.queue.execute_all(filesystem)
}
pub(super) fn execute_up_to(
&mut self,
filesystem: &crate::fs_utils::FilesystemConfig,
max_tasks: NonZeroUsize,
) -> Vec<crate::plugin::PluginWorkspaceObserveTaskCompletion> {
self.queue.execute_up_to(filesystem, max_tasks)
}
pub(super) fn execute_next(
&mut self,
filesystem: &crate::fs_utils::FilesystemConfig,
) -> Option<crate::plugin::PluginWorkspaceObserveTaskCompletion> {
self.queue.execute_next(filesystem)
}
pub(super) fn poll(
&self,
handle: &PluginWorkspaceObserveTaskHandle,
) -> PluginWorkspaceObserveTaskPoll {
self.queue.poll(handle)
}
pub(super) fn take(
&mut self,
handle: &PluginWorkspaceObserveTaskHandle,
) -> PluginWorkspaceObserveTaskTake {
self.queue.take(handle)
}
pub(super) fn push_resource_started_in_update(
&mut self,
table: &mut ResourceTable,
resource_authority: PluginWorkspaceObserveTaskResourceAuthority,
) -> Result<Resource<PluginWorkspaceObserveTaskHandle>, PluginHostImportError> {
let resource_authority = resource_authority.into_parts();
let resource = self.resources.push(
table,
resource_authority.handle,
resource_authority.authority_path,
)?;
self.resources_started_in_update.record(&resource);
Ok(resource)
}
pub(super) fn resolve_authorized_resource(
&mut self,
table: &ResourceTable,
session: &PluginHostImportSession,
resource: &Resource<PluginWorkspaceObserveTaskHandle>,
) -> Result<PluginWorkspaceObserveTaskHandle, PluginHostImportError> {
self.resources.resolve_authorized(table, session, resource)
}
pub(super) fn delete_resource(
&mut self,
table: &mut ResourceTable,
resource: Resource<PluginWorkspaceObserveTaskHandle>,
) -> Result<WasmtimeWorkspaceObserveTaskResourceDrop, PluginHostImportError> {
let rep = WasmtimeWorkspaceObserveTaskResourceRep::from_resource(&resource);
let dropped = self.resources.delete(table, resource)?;
self.forget_started_resource(rep);
Ok(dropped)
}
pub(super) fn forget_started_resource(&mut self, rep: WasmtimeWorkspaceObserveTaskResourceRep) {
self.resources_started_in_update.forget(rep);
}
pub(super) fn commit_started_in_update(&mut self) {
self.resources_started_in_update.commit();
}
pub(super) fn invalidate_started_in_update(&mut self) -> usize {
let reps = self.resources_started_in_update.take_for_invalidation();
self.resources.invalidate_reps(&reps)
}
pub(super) fn cancel_identity(
&mut self,
identity: &PluginIdentity,
) -> PluginWorkspaceObserveTaskCancellationReport {
let report = self.queue.cancel_identity(identity);
let _invalidated_resources = self.resources.cancel_identity(identity);
self.resources_started_in_update
.retain_not_belonging_to(&self.resources, identity);
report
}
pub(super) fn cancel_all(&mut self) -> Vec<PluginWorkspaceObserveTaskCancellationReport> {
let reports = self.queue.cancel_all();
let _invalidated_resources = self.resources.cancel_all();
self.resources_started_in_update.clear();
reports
}
#[cfg(test)]
pub(super) fn pending_len(&self) -> usize {
self.queue.pending_len()
}
#[cfg(test)]
pub(super) fn reserved_pending_len(&self) -> usize {
self.queue.reserved_pending_len()
}
#[cfg(test)]
pub(super) fn retained_len(&self) -> usize {
self.queue.retained_len()
}
#[cfg(test)]
pub(super) fn live_resource_len(&self) -> usize {
self.resources.live_len()
}
#[cfg(test)]
pub(super) fn tracked_resource_reps(&self) -> Vec<WasmtimeWorkspaceObserveTaskResourceRep> {
self.resources
.resources
.iter()
.map(|resource| resource.rep)
.collect()
}
}
#[derive(Debug)]
pub(super) struct WasmtimeStartedWorkspaceObserveTaskResources {
reps: Vec<WasmtimeWorkspaceObserveTaskResourceRep>,
}
impl WasmtimeStartedWorkspaceObserveTaskResources {
pub(super) const fn new() -> Self {
Self { reps: Vec::new() }
}
pub(super) fn record(&mut self, resource: &Resource<PluginWorkspaceObserveTaskHandle>) {
self.reps
.push(WasmtimeWorkspaceObserveTaskResourceRep::from_resource(
resource,
));
}
pub(super) fn forget(&mut self, rep: WasmtimeWorkspaceObserveTaskResourceRep) {
self.reps.retain(|started| *started != rep);
}
pub(super) fn commit(&mut self) {
self.reps.clear();
}
pub(super) fn take_for_invalidation(&mut self) -> Vec<WasmtimeWorkspaceObserveTaskResourceRep> {
std::mem::take(&mut self.reps)
}
pub(super) fn retain_not_belonging_to(
&mut self,
resources: &WasmtimeWorkspaceObserveTaskResources,
identity: &PluginIdentity,
) {
self.reps
.retain(|rep| !resources.resource_belongs_to_identity(*rep, identity));
}
pub(super) fn clear(&mut self) {
self.reps.clear();
}
}
#[derive(Debug)]
pub(super) struct WasmtimeWorkspaceObserveTaskResources {
pub(super) resources: Vec<WasmtimeWorkspaceObserveTaskResource>,
limit: WasmtimeWorkspaceObserveTaskResourceLimit,
}
impl WasmtimeWorkspaceObserveTaskResources {
pub(super) const fn new(limit: PluginWorkspaceObserveTaskQueueLimit) -> Self {
Self {
resources: Vec::new(),
limit: WasmtimeWorkspaceObserveTaskResourceLimit::from_queue_limit(limit),
}
}
pub(super) fn push(
&mut self,
table: &mut ResourceTable,
handle: PluginWorkspaceObserveTaskHandle,
authority_path: WorkspacePath,
) -> Result<Resource<PluginWorkspaceObserveTaskHandle>, PluginHostImportError> {
if self.resources.len() >= self.limit.get() {
return Err(PluginHostImportError::ResourceHandle {
kind: PluginHostResourceKind::WorkspaceObserveTask,
reason: PluginResourceHandleRejectionReason::Unavailable,
});
}
let resource = table
.push(handle.clone())
.map_err(|source| workspace_observe_task_resource_error(&source))?;
let rep = WasmtimeWorkspaceObserveTaskResourceRep::from_resource(&resource);
if let Some(index) = self.resource_index(rep) {
let _invalidated = self.resources[index].invalidate();
let _handle = table
.delete(resource)
.map_err(|source| workspace_observe_task_resource_error(&source))?;
return Err(workspace_observe_task_resource_unavailable());
}
self.resources
.push(WasmtimeWorkspaceObserveTaskResource::new(
rep,
handle,
authority_path,
));
Ok(resource)
}
pub(super) fn resolve_authorized(
&mut self,
table: &ResourceTable,
session: &PluginHostImportSession,
resource: &Resource<PluginWorkspaceObserveTaskHandle>,
) -> Result<PluginWorkspaceObserveTaskHandle, PluginHostImportError> {
let rep = WasmtimeWorkspaceObserveTaskResourceRep::from_resource(resource);
let Some(index) = self.resource_index(rep) else {
return Err(workspace_observe_task_resource_error(
&ResourceTableError::NotPresent,
));
};
if !self.resources[index].is_active() {
return Err(workspace_observe_task_resource_error(
&ResourceTableError::NotPresent,
));
}
if let Err(error) = session.authorize_workspace_observe_task_resource(
self.resources[index].authority_path.as_ref(),
) {
let _invalidated = self.resources[index].invalidate();
return Err(error);
}
let handle = match table.get(resource) {
Ok(handle) => handle.clone(),
Err(source) => {
let _invalidated = self.resources[index].invalidate();
return Err(workspace_observe_task_resource_error(&source));
}
};
if handle != self.resources[index].handle {
let _invalidated = self.resources[index].invalidate();
return Err(workspace_observe_task_resource_unavailable());
}
Ok(handle)
}
pub(super) fn delete(
&mut self,
table: &mut ResourceTable,
resource: Resource<PluginWorkspaceObserveTaskHandle>,
) -> Result<WasmtimeWorkspaceObserveTaskResourceDrop, PluginHostImportError> {
let rep = WasmtimeWorkspaceObserveTaskResourceRep::from_resource(&resource);
match table.delete(resource) {
Ok(handle) => {
let Some(tracked) = self.remove_rep(rep) else {
return Err(workspace_observe_task_resource_unavailable());
};
if handle != tracked.handle {
return Err(workspace_observe_task_resource_unavailable());
}
Ok(WasmtimeWorkspaceObserveTaskResourceDrop::from_tracked(
tracked,
))
}
Err(ResourceTableError::NotPresent) => Ok(self.remove_rep(rep).map_or(
WasmtimeWorkspaceObserveTaskResourceDrop::Inactive,
WasmtimeWorkspaceObserveTaskResourceDrop::from_tracked,
)),
Err(source) => {
if let Some(index) = self.resource_index(rep) {
let _invalidated = self.resources[index].invalidate();
}
Err(workspace_observe_task_resource_error(&source))
}
}
}
pub(super) fn cancel_identity(&mut self, identity: &PluginIdentity) -> usize {
let mut invalidated = 0;
for resource in &mut self.resources {
if resource.handle.identity_proof() == identity && resource.invalidate() {
invalidated += 1;
}
}
invalidated
}
pub(super) fn cancel_all(&mut self) -> usize {
let mut invalidated = 0;
for resource in &mut self.resources {
if resource.invalidate() {
invalidated += 1;
}
}
invalidated
}
pub(super) fn invalidate_reps(
&mut self,
reps: &[WasmtimeWorkspaceObserveTaskResourceRep],
) -> usize {
let mut invalidated = 0;
for rep in reps {
let Some(index) = self.resource_index(*rep) else {
continue;
};
if self.resources[index].invalidate() {
invalidated += 1;
}
}
invalidated
}
#[must_use]
#[cfg(test)]
pub(super) fn live_len(&self) -> usize {
self.resources
.iter()
.filter(|resource| resource.is_active())
.count()
}
pub(super) fn remove_rep(
&mut self,
rep: WasmtimeWorkspaceObserveTaskResourceRep,
) -> Option<WasmtimeWorkspaceObserveTaskResource> {
let index = self.resource_index(rep)?;
Some(self.resources.remove(index))
}
pub(super) fn resource_index(
&self,
rep: WasmtimeWorkspaceObserveTaskResourceRep,
) -> Option<usize> {
self.resources
.iter()
.position(|resource| resource.rep == rep)
}
pub(super) fn resource_belongs_to_identity(
&self,
rep: WasmtimeWorkspaceObserveTaskResourceRep,
identity: &PluginIdentity,
) -> bool {
self.resource_index(rep)
.is_some_and(|index| self.resources[index].handle.identity_proof() == identity)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) enum WasmtimeWorkspaceObserveTaskResourceDrop {
Active(PluginWorkspaceObserveTaskHandle),
Inactive,
}
impl WasmtimeWorkspaceObserveTaskResourceDrop {
pub(super) fn from_tracked(resource: WasmtimeWorkspaceObserveTaskResource) -> Self {
if resource.is_active() {
Self::Active(resource.handle)
} else {
Self::Inactive
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) struct WasmtimeWorkspaceObserveTaskResourceLimit(NonZeroUsize);
impl WasmtimeWorkspaceObserveTaskResourceLimit {
pub(super) const fn from_queue_limit(limit: PluginWorkspaceObserveTaskQueueLimit) -> Self {
Self(limit.max_resource_slots_limit())
}
pub(super) const fn get(self) -> usize {
self.0.get()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) struct WasmtimeWorkspaceObserveTaskResourceRep(u32);
impl WasmtimeWorkspaceObserveTaskResourceRep {
pub(super) fn from_resource<T>(resource: &Resource<T>) -> Self {
Self(resource.rep())
}
#[cfg(test)]
pub(super) const fn raw(self) -> u32 {
self.0
}
#[cfg(test)]
pub(super) fn borrowed_task(self) -> Resource<PluginWorkspaceObserveTaskHandle> {
Resource::new_borrow(self.0)
}
#[cfg(test)]
pub(super) fn owned_task(self) -> Resource<PluginWorkspaceObserveTaskHandle> {
Resource::new_own(self.0)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) struct WasmtimeWorkspaceObserveTaskResource {
pub(super) rep: WasmtimeWorkspaceObserveTaskResourceRep,
pub(super) handle: PluginWorkspaceObserveTaskHandle,
pub(super) authority_path: WorkspacePath,
pub(super) state: WasmtimeWorkspaceObserveTaskResourceState,
}
impl WasmtimeWorkspaceObserveTaskResource {
pub(super) const fn new(
rep: WasmtimeWorkspaceObserveTaskResourceRep,
handle: PluginWorkspaceObserveTaskHandle,
authority_path: WorkspacePath,
) -> Self {
Self {
rep,
handle,
authority_path,
state: WasmtimeWorkspaceObserveTaskResourceState::Active,
}
}
pub(super) const fn is_active(&self) -> bool {
matches!(
self.state,
WasmtimeWorkspaceObserveTaskResourceState::Active
)
}
pub(super) const fn invalidate(&mut self) -> bool {
if !self.is_active() {
return false;
}
self.state = WasmtimeWorkspaceObserveTaskResourceState::Invalidated;
true
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum WasmtimeWorkspaceObserveTaskResourceState {
Active,
Invalidated,
}
pub(super) fn workspace_observe_poll_to_guest(
poll: PluginWorkspaceObserveTaskPoll,
) -> wit_host::WorkspaceObserveTaskPoll {
match poll {
PluginWorkspaceObserveTaskPoll::Pending => wit_host::WorkspaceObserveTaskPoll::Pending,
PluginWorkspaceObserveTaskPoll::Completed { outcome } => {
wit_host::WorkspaceObserveTaskPoll::Completed(workspace_observe_shape_to_guest(outcome))
}
PluginWorkspaceObserveTaskPoll::Unknown => wit_host::WorkspaceObserveTaskPoll::Unknown,
}
}
pub(super) fn workspace_observe_take_to_guest(
take: PluginWorkspaceObserveTaskTake,
) -> wit_host::WorkspaceObserveTaskTake {
match take {
PluginWorkspaceObserveTaskTake::Pending => wit_host::WorkspaceObserveTaskTake::Pending,
PluginWorkspaceObserveTaskTake::Completed(PluginWorkspaceObserveOutcome::Bytes(bytes)) => {
wit_host::WorkspaceObserveTaskTake::Bytes(bytes.into_vec())
}
PluginWorkspaceObserveTaskTake::Completed(PluginWorkspaceObserveOutcome::Rejected) => {
wit_host::WorkspaceObserveTaskTake::Rejected
}
PluginWorkspaceObserveTaskTake::Unknown => wit_host::WorkspaceObserveTaskTake::Unknown,
}
}
fn workspace_observe_shape_to_guest(
shape: PluginWorkspaceObserveOutcomeShape,
) -> wit_host::WorkspaceObserveOutcomeShape {
match shape {
PluginWorkspaceObserveOutcomeShape::Bytes { byte_len } => {
wit_host::WorkspaceObserveOutcomeShape::Bytes(
u64::try_from(byte_len).unwrap_or(u64::MAX),
)
}
PluginWorkspaceObserveOutcomeShape::Rejected => {
wit_host::WorkspaceObserveOutcomeShape::Rejected
}
}
}