use super::{PluginWorkspaceReadByteLimit, PluginWorkspaceWriteByteLimit};
use crate::{
fs_utils::{
EscapedDisplayText, FileReadError, FileWriteError, FilesystemConfig, ReadTextFile,
atomic_write_workspace_file, read_existing_workspace_file,
},
plugin::{PluginIdentity, WorkspacePath, WorkspacePathRef},
};
use std::fmt::{Debug, Display, Formatter};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WorkspaceAccessKind {
Read,
Write,
}
impl WorkspaceAccessKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Read => "read",
Self::Write => "write",
}
}
}
impl Display for WorkspaceAccessKind {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Eq, PartialEq)]
pub struct AuthorizedWorkspaceReadAccess {
identity: PluginIdentity,
path: WorkspacePath,
}
impl AuthorizedWorkspaceReadAccess {
#[must_use]
pub(in crate::plugin::host) const fn new(
identity: PluginIdentity,
path: WorkspacePath,
) -> Self {
Self { identity, path }
}
#[must_use]
pub fn identity(&self) -> &str {
self.identity_proof().as_str()
}
#[must_use]
pub const fn identity_proof(&self) -> &PluginIdentity {
&self.identity
}
#[must_use]
pub const fn kind(&self) -> WorkspaceAccessKind {
WorkspaceAccessKind::Read
}
#[must_use]
pub const fn workspace_path(&self) -> WorkspacePathRef<'_> {
self.path.as_ref()
}
#[must_use]
pub fn workspace_relative_path(&self) -> &str {
self.path.as_str()
}
#[must_use]
pub(in crate::plugin::host::workspace_io) fn into_policy_token(
self,
) -> PluginWorkspaceReadToken {
PluginWorkspaceReadToken {
identity: self.identity,
path: self.path,
}
}
}
impl Debug for AuthorizedWorkspaceReadAccess {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
fmt_workspace_access(
formatter,
"AuthorizedWorkspaceReadAccess",
&self.identity,
self.kind(),
self.path.as_ref(),
)
}
}
#[derive(Eq, PartialEq)]
pub struct AuthorizedWorkspaceWriteAccess {
identity: PluginIdentity,
path: WorkspacePath,
}
impl AuthorizedWorkspaceWriteAccess {
#[must_use]
pub(in crate::plugin::host) const fn new(
identity: PluginIdentity,
path: WorkspacePath,
) -> Self {
Self { identity, path }
}
#[must_use]
pub fn identity(&self) -> &str {
self.identity_proof().as_str()
}
#[must_use]
pub const fn identity_proof(&self) -> &PluginIdentity {
&self.identity
}
#[must_use]
pub const fn kind(&self) -> WorkspaceAccessKind {
WorkspaceAccessKind::Write
}
#[must_use]
pub const fn workspace_path(&self) -> WorkspacePathRef<'_> {
self.path.as_ref()
}
#[must_use]
pub fn workspace_relative_path(&self) -> &str {
self.path.as_str()
}
#[must_use]
pub(in crate::plugin::host::workspace_io) fn into_policy_token(
self,
) -> PluginWorkspaceWriteToken {
PluginWorkspaceWriteToken {
identity: self.identity,
path: self.path,
}
}
}
impl Debug for AuthorizedWorkspaceWriteAccess {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
fmt_workspace_access(
formatter,
"AuthorizedWorkspaceWriteAccess",
&self.identity,
self.kind(),
self.path.as_ref(),
)
}
}
#[derive(Eq, PartialEq)]
pub(in crate::plugin::host::workspace_io) struct PluginWorkspaceReadToken {
identity: PluginIdentity,
path: WorkspacePath,
}
impl PluginWorkspaceReadToken {
#[must_use]
pub const fn workspace_path(&self) -> WorkspacePathRef<'_> {
self.path.as_ref()
}
#[must_use]
pub fn workspace_relative_path(&self) -> &str {
self.path.as_str()
}
pub(in crate::plugin::host::workspace_io) fn read_existing(
self,
filesystem: &FilesystemConfig,
max_bytes: PluginWorkspaceReadByteLimit,
) -> Result<ReadTextFile, PluginWorkspaceReadError> {
read_existing_workspace_file(self.path.as_str(), filesystem, max_bytes.get())
.map_err(PluginWorkspaceReadError::Read)
}
}
impl Debug for PluginWorkspaceReadToken {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
fmt_workspace_access(
formatter,
"PluginWorkspaceReadToken",
&self.identity,
WorkspaceAccessKind::Read,
self.path.as_ref(),
)
}
}
#[derive(Eq, PartialEq)]
pub(in crate::plugin::host::workspace_io) struct PluginWorkspaceWriteToken {
identity: PluginIdentity,
path: WorkspacePath,
}
impl PluginWorkspaceWriteToken {
#[must_use]
pub const fn workspace_path(&self) -> WorkspacePathRef<'_> {
self.path.as_ref()
}
#[must_use]
pub fn workspace_relative_path(&self) -> &str {
self.path.as_str()
}
pub(in crate::plugin::host::workspace_io) fn write_atomic(
self,
bytes: &[u8],
filesystem: &FilesystemConfig,
max_bytes: PluginWorkspaceWriteByteLimit,
) -> Result<EscapedDisplayText, PluginWorkspaceWriteError> {
let size = u64::try_from(bytes.len()).unwrap_or(u64::MAX);
if size > max_bytes.get() {
return Err(PluginWorkspaceWriteError::PayloadTooLarge {
size,
max_size: max_bytes.get(),
});
}
atomic_write_workspace_file(self.path.as_str(), bytes, filesystem)
.map(|path| EscapedDisplayText::from_path(&path))
.map_err(PluginWorkspaceWriteError::Write)
}
}
impl Debug for PluginWorkspaceWriteToken {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
fmt_workspace_access(
formatter,
"PluginWorkspaceWriteToken",
&self.identity,
WorkspaceAccessKind::Write,
self.path.as_ref(),
)
}
}
fn fmt_workspace_access(
formatter: &mut Formatter<'_>,
type_name: &'static str,
identity: &PluginIdentity,
kind: WorkspaceAccessKind,
path: WorkspacePathRef<'_>,
) -> std::fmt::Result {
formatter
.debug_struct(type_name)
.field("identity", identity)
.field("kind", &kind)
.field("path_byte_len", &path.as_str().len())
.finish()
}
#[derive(thiserror::Error)]
#[non_exhaustive]
pub enum PluginWorkspaceReadError {
Read(#[source] FileReadError),
}
impl Debug for PluginWorkspaceReadError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Read(_source) => formatter.write_str("Read"),
}
}
}
impl Display for PluginWorkspaceReadError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Read(_source) => formatter.write_str("plugin workspace read denied"),
}
}
}
#[derive(thiserror::Error)]
#[non_exhaustive]
pub enum PluginWorkspaceWriteError {
PayloadTooLarge {
size: u64,
max_size: u64,
},
Write(#[source] FileWriteError),
}
impl Debug for PluginWorkspaceWriteError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::PayloadTooLarge { size, max_size } => formatter
.debug_struct("PayloadTooLarge")
.field("size", size)
.field("max_size", max_size)
.finish(),
Self::Write(_source) => formatter.write_str("Write"),
}
}
}
impl Display for PluginWorkspaceWriteError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::PayloadTooLarge { size, max_size } => write!(
formatter,
"plugin workspace payload is {size} bytes, exceeding the {max_size} byte limit"
),
Self::Write(_source) => formatter.write_str("plugin workspace write denied"),
}
}
}