Skip to main content

kcode_k1_web_code_session/
lib.rs

1use kcode_k1_web_code_document::{CodeDocument, DocumentError};
2use kcode_k1_web_package::SourcePackage;
3use std::{
4    error::Error,
5    fmt::{Display, Formatter},
6    sync::Arc,
7};
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum DocumentPart {
11    Documentation,
12    Code,
13}
14
15#[derive(Clone)]
16pub struct SessionScope {
17    identity: Arc<()>,
18}
19
20#[derive(Clone)]
21pub struct OpenedLibrary {
22    scope: Arc<()>,
23    user: [u8; 12],
24    source: Arc<SourcePackage>,
25    document: CodeDocument,
26}
27
28impl OpenedLibrary {
29    pub const fn document(&self) -> &CodeDocument {
30        &self.document
31    }
32}
33
34#[derive(Clone)]
35pub struct CheckEvidence {
36    scope: Arc<()>,
37    user: [u8; 12],
38    source: Arc<SourcePackage>,
39}
40
41#[derive(Clone, Debug, Eq, PartialEq)]
42pub enum SessionError {
43    ForeignHandle,
44    StaleSource,
45    InvalidDocument(String),
46}
47
48impl Display for SessionError {
49    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
50        match self {
51            Self::ForeignHandle => formatter.write_str("foreign handle"),
52            Self::StaleSource => formatter.write_str("stale source"),
53            Self::InvalidDocument(message) => formatter.write_str(message),
54        }
55    }
56}
57
58impl Error for SessionError {}
59
60impl From<DocumentError> for SessionError {
61    fn from(error: DocumentError) -> Self {
62        Self::InvalidDocument(error.to_string())
63    }
64}
65
66impl Default for SessionScope {
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72impl SessionScope {
73    pub fn new() -> Self {
74        Self {
75            identity: Arc::new(()),
76        }
77    }
78
79    pub fn recover(
80        &self,
81        user: [u8; 12],
82        source: Arc<SourcePackage>,
83    ) -> Result<OpenedLibrary, SessionError> {
84        self.open(user, source)
85    }
86
87    pub fn refresh(
88        &self,
89        user: [u8; 12],
90        source: Arc<SourcePackage>,
91    ) -> Result<OpenedLibrary, SessionError> {
92        self.open(user, source)
93    }
94
95    pub fn validate(
96        &self,
97        user: [u8; 12],
98        opened: &OpenedLibrary,
99        current: &SourcePackage,
100    ) -> Result<(), SessionError> {
101        if !Arc::ptr_eq(&self.identity, &opened.scope) || opened.user != user {
102            return Err(SessionError::ForeignHandle);
103        }
104        if opened.source.as_ref() != current {
105            return Err(SessionError::StaleSource);
106        }
107        Ok(())
108    }
109
110    pub fn replacement(
111        &self,
112        user: [u8; 12],
113        opened: &OpenedLibrary,
114        current: &SourcePackage,
115        part: DocumentPart,
116        replacement: String,
117    ) -> Result<SourcePackage, SessionError> {
118        self.validate(user, opened, current)?;
119
120        let document = opened.document();
121        let (documentation, code) = match part {
122            DocumentPart::Documentation => (replacement.into_bytes(), document.code().to_vec()),
123            DocumentPart::Code => (document.documentation().to_vec(), replacement.into_bytes()),
124        };
125        let replacement = CodeDocument::new(
126            document.id().clone(),
127            documentation,
128            document.language(),
129            code,
130        )?;
131        replacement.to_source_package().map_err(Into::into)
132    }
133
134    pub fn mint_evidence(
135        &self,
136        user: [u8; 12],
137        opened: &OpenedLibrary,
138        current: &SourcePackage,
139    ) -> Result<CheckEvidence, SessionError> {
140        self.validate(user, opened, current)?;
141        Ok(CheckEvidence {
142            scope: Arc::clone(&self.identity),
143            user,
144            source: Arc::clone(&opened.source),
145        })
146    }
147
148    pub fn evidence_matches(
149        &self,
150        user: [u8; 12],
151        opened: &OpenedLibrary,
152        current: &SourcePackage,
153        evidence: &CheckEvidence,
154    ) -> Result<bool, SessionError> {
155        self.validate(user, opened, current)?;
156        Ok(Arc::ptr_eq(&self.identity, &evidence.scope)
157            && evidence.user == user
158            && evidence.source.as_ref() == current)
159    }
160
161    fn open(
162        &self,
163        user: [u8; 12],
164        source: Arc<SourcePackage>,
165    ) -> Result<OpenedLibrary, SessionError> {
166        let document = CodeDocument::from_source_package(source.as_ref())?;
167        Ok(OpenedLibrary {
168            scope: Arc::clone(&self.identity),
169            user,
170            source,
171            document,
172        })
173    }
174}