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