kcode-k1-web-code-session 0.1.0

Own pure session-bound Web-code handles and document preparation
Documentation
use kcode_k1_web_code_document::{CodeDocument, DocumentError};
use kcode_k1_web_package::SourcePackage;
use std::{
    error::Error,
    fmt::{Display, Formatter},
    sync::Arc,
};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DocumentPart {
    Documentation,
    Code,
}

#[derive(Clone)]
pub struct SessionScope {
    identity: Arc<()>,
}

#[derive(Clone)]
pub struct OpenedLibrary {
    scope: Arc<()>,
    user: [u8; 12],
    source: Arc<SourcePackage>,
    document: CodeDocument,
}

impl OpenedLibrary {
    pub const fn document(&self) -> &CodeDocument {
        &self.document
    }
}

#[derive(Clone)]
pub struct CheckEvidence {
    scope: Arc<()>,
    user: [u8; 12],
    source: Arc<SourcePackage>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SessionError {
    ForeignHandle,
    StaleSource,
    InvalidDocument(String),
}

impl Display for SessionError {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ForeignHandle => formatter.write_str("foreign handle"),
            Self::StaleSource => formatter.write_str("stale source"),
            Self::InvalidDocument(message) => formatter.write_str(message),
        }
    }
}

impl Error for SessionError {}

impl From<DocumentError> for SessionError {
    fn from(error: DocumentError) -> Self {
        Self::InvalidDocument(error.to_string())
    }
}

impl Default for SessionScope {
    fn default() -> Self {
        Self::new()
    }
}

impl SessionScope {
    pub fn new() -> Self {
        Self {
            identity: Arc::new(()),
        }
    }

    pub fn recover(
        &self,
        user: [u8; 12],
        source: Arc<SourcePackage>,
    ) -> Result<OpenedLibrary, SessionError> {
        self.open(user, source)
    }

    pub fn refresh(
        &self,
        user: [u8; 12],
        source: Arc<SourcePackage>,
    ) -> Result<OpenedLibrary, SessionError> {
        self.open(user, source)
    }

    pub fn validate(
        &self,
        user: [u8; 12],
        opened: &OpenedLibrary,
        current: &SourcePackage,
    ) -> Result<(), SessionError> {
        if !Arc::ptr_eq(&self.identity, &opened.scope) || opened.user != user {
            return Err(SessionError::ForeignHandle);
        }
        if opened.source.as_ref() != current {
            return Err(SessionError::StaleSource);
        }
        Ok(())
    }

    pub fn replacement(
        &self,
        user: [u8; 12],
        opened: &OpenedLibrary,
        current: &SourcePackage,
        part: DocumentPart,
        replacement: String,
    ) -> Result<SourcePackage, SessionError> {
        self.validate(user, opened, current)?;

        let document = opened.document();
        let (documentation, code) = match part {
            DocumentPart::Documentation => (replacement.into_bytes(), document.code().to_vec()),
            DocumentPart::Code => (document.documentation().to_vec(), replacement.into_bytes()),
        };
        let replacement = CodeDocument::new(
            document.id().clone(),
            documentation,
            document.language(),
            code,
        )?;
        replacement.to_source_package().map_err(Into::into)
    }

    pub fn mint_evidence(
        &self,
        user: [u8; 12],
        opened: &OpenedLibrary,
        current: &SourcePackage,
    ) -> Result<CheckEvidence, SessionError> {
        self.validate(user, opened, current)?;
        Ok(CheckEvidence {
            scope: Arc::clone(&self.identity),
            user,
            source: Arc::clone(&opened.source),
        })
    }

    pub fn evidence_matches(
        &self,
        user: [u8; 12],
        opened: &OpenedLibrary,
        current: &SourcePackage,
        evidence: &CheckEvidence,
    ) -> Result<bool, SessionError> {
        self.validate(user, opened, current)?;
        Ok(Arc::ptr_eq(&self.identity, &evidence.scope)
            && evidence.user == user
            && evidence.source.as_ref() == current)
    }

    fn open(
        &self,
        user: [u8; 12],
        source: Arc<SourcePackage>,
    ) -> Result<OpenedLibrary, SessionError> {
        let document = CodeDocument::from_source_package(source.as_ref())?;
        Ok(OpenedLibrary {
            scope: Arc::clone(&self.identity),
            user,
            source,
            document,
        })
    }
}