kcode_k1_web_code_session/
lib.rs1use 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}