1use super::error::{ProtocolError, ProtocolResult};
35use super::format::{format_document, format_link};
36use super::mapping::LinoDocument;
37use crate::changes_simplifier::simplify_changes;
38use crate::link::Link;
39use crate::named_type_links::NamedTypeLinks;
40use links_notation::LiNo;
41
42pub const ANY: &str = "*";
44
45pub type Part = Option<u32>;
47
48#[derive(Clone, Debug, PartialEq, Eq)]
50pub enum LinksOperation {
51 Count(Vec<Part>),
53 Each(Vec<Part>),
55 Create { source: u32, target: u32 },
57 Update {
60 index: u32,
61 source: u32,
62 target: u32,
63 },
64 Delete(u32),
66 GetName(u32),
68 SetName(u32, String),
70 GetByName(String),
72 RemoveName(u32),
74}
75
76pub type Change = (Link, Link);
78
79impl LinksOperation {
80 pub fn to_document(&self) -> LinoDocument {
82 let (name, argument) = match self {
83 Self::Count(restriction) => ("count", restriction_lino(restriction)),
84 Self::Each(restriction) => ("each", restriction_lino(restriction)),
85 Self::Create { source, target } => ("create", numbers_lino(&[*source, *target])),
86 Self::Update {
87 index,
88 source,
89 target,
90 } => ("update", numbers_lino(&[*index, *source, *target])),
91 Self::Delete(index) => ("delete", number(*index)),
92 Self::GetName(index) => ("get-name", number(*index)),
93 Self::SetName(index, name) => (
94 "set-name",
95 group(vec![number(*index), LiNo::Ref(name.clone())]),
96 ),
97 Self::GetByName(name) => ("get-by-name", LiNo::Ref(name.clone())),
98 Self::RemoveName(index) => ("remove-name", number(*index)),
99 };
100 vec![named(name, vec![argument])]
101 }
102
103 pub fn from_document(document: &[LiNo<String>]) -> ProtocolResult<Option<Self>> {
106 let [LiNo::Link {
107 id: Some(name),
108 values,
109 }] = document
110 else {
111 return Ok(None);
112 };
113 let [argument] = values.as_slice() else {
114 return Ok(None);
115 };
116 let operation = match name.as_str() {
117 "count" => Self::Count(parse_restriction(argument)?),
118 "each" => Self::Each(parse_restriction(argument)?),
119 "create" => {
120 let [source, target] = parse_numbers::<2>(argument)?;
121 Self::Create { source, target }
122 }
123 "update" => {
124 let [index, source, target] = parse_numbers::<3>(argument)?;
125 Self::Update {
126 index,
127 source,
128 target,
129 }
130 }
131 "delete" => Self::Delete(parse_number(argument)?),
132 "get-name" => Self::GetName(parse_number(argument)?),
133 "set-name" => match parts(argument) {
134 [index, LiNo::Ref(name)] => Self::SetName(parse_number(index)?, name.clone()),
135 _ => return Err(malformed_argument("set-name", argument)),
136 },
137 "get-by-name" => match argument {
138 LiNo::Ref(name) => Self::GetByName(name.clone()),
139 _ => return Err(malformed_argument("get-by-name", argument)),
140 },
141 "remove-name" => Self::RemoveName(parse_number(argument)?),
142 _ => return Ok(None),
143 };
144 Ok(Some(operation))
145 }
146
147 pub fn execute<S: NamedTypeLinks + ?Sized>(
149 &self,
150 storage: &mut S,
151 ) -> anyhow::Result<LinoDocument> {
152 Ok(match self {
153 Self::Count(restriction) => {
154 let count = matching(storage, restriction).len();
155 vec![named("count", vec![LiNo::Ref(count.to_string())])]
156 }
157 Self::Each(restriction) => matching(storage, restriction)
158 .iter()
159 .map(link_lino)
160 .collect(),
161 Self::Create { source, target } => {
162 let created = Link::new(storage.create(*source, *target), *source, *target);
164 storage.save()?;
165 changes_document(&[(Link::null(), created)])
166 }
167 Self::Update {
168 index,
169 source,
170 target,
171 } => {
172 let mut changes = Vec::new();
173 storage.update_observed(*index, *source, *target, &mut |before, after| {
174 changes.push((before, after))
175 })?;
176 storage.save()?;
177 changes_document(&simplify_changes(changes))
178 }
179 Self::Delete(index) => {
180 let mut changes = Vec::new();
181 storage
182 .delete_observed(*index, &mut |before, after| changes.push((before, after)))?;
183 storage.save()?;
184 changes_document(&simplify_changes(changes))
185 }
186 Self::GetName(index) => match storage.get_name(*index)? {
187 Some(name) => vec![named("name", vec![LiNo::Ref(name)])],
188 None => Vec::new(),
189 },
190 Self::SetName(index, name) => {
191 let link = storage.set_name(*index, name)?;
192 storage.save()?;
193 link_reply(Some(link))
194 }
195 Self::GetByName(name) => link_reply(storage.get_by_name(name)?),
196 Self::RemoveName(index) => {
197 storage.remove_name(*index)?;
198 storage.save()?;
199 Vec::new()
200 }
201 })
202 }
203}
204
205pub fn matches(link: &Link, restriction: &[Part]) -> bool {
208 let is = |part: Part, value: u32| part.is_none_or(|part| part == value);
209 match *restriction {
210 [] => true,
211 [index] => is(index, link.index),
212 [index, value] => {
213 is(index, link.index) && (is(value, link.source) || is(value, link.target))
214 }
215 [index, source, target] => {
216 is(index, link.index) && is(source, link.source) && is(target, link.target)
217 }
218 _ => false,
219 }
220}
221
222fn matching<S: NamedTypeLinks + ?Sized>(storage: &mut S, restriction: &[Part]) -> Vec<Link> {
223 let mut links = match restriction.first() {
224 Some(Some(index)) => storage.get_link(*index).into_iter().collect(),
225 _ => storage.all_links(),
226 };
227 links.retain(|link| matches(link, restriction));
228 links.sort_by_key(|link| link.index);
229 links
230}
231
232pub fn parse_count(document: &[LiNo<String>]) -> ProtocolResult<u32> {
234 match document {
235 [LiNo::Link {
236 id: Some(id),
237 values,
238 }] if id == "count" => match values.as_slice() {
239 [count] => parse_number(count),
240 _ => Err(malformed_reply("count", document)),
241 },
242 _ => Err(malformed_reply("count", document)),
243 }
244}
245
246pub fn parse_links(document: &[LiNo<String>]) -> ProtocolResult<Vec<Link>> {
248 document.iter().map(parse_link).collect()
249}
250
251pub fn parse_changes(document: &[LiNo<String>]) -> ProtocolResult<Vec<Change>> {
253 document
254 .iter()
255 .map(|change| match parts(change) {
256 [before, after] => Ok((parse_change_side(before)?, parse_change_side(after)?)),
257 _ => Err(malformed_reply("change", document)),
258 })
259 .collect()
260}
261
262pub fn parse_name(document: &[LiNo<String>]) -> ProtocolResult<Option<String>> {
264 match document {
265 [] => Ok(None),
266 [LiNo::Link {
267 id: Some(id),
268 values,
269 }] if id == "name" => match values.as_slice() {
270 [LiNo::Ref(name)] => Ok(Some(name.clone())),
271 _ => Err(malformed_reply("name", document)),
272 },
273 _ => Err(malformed_reply("name", document)),
274 }
275}
276
277pub fn parse_link_reply(document: &[LiNo<String>]) -> ProtocolResult<Option<u32>> {
279 match document {
280 [] => Ok(None),
281 [LiNo::Link {
282 id: Some(id),
283 values,
284 }] if id == "link" => match values.as_slice() {
285 [link] => parse_number(link).map(Some),
286 _ => Err(malformed_reply("link", document)),
287 },
288 _ => Err(malformed_reply("link", document)),
289 }
290}
291
292pub fn changes_document(changes: &[Change]) -> LinoDocument {
294 changes
295 .iter()
296 .map(|(before, after)| group(vec![change_side(before), change_side(after)]))
297 .collect()
298}
299
300pub fn link_lino(link: &Link) -> LiNo<String> {
302 named(
303 &link.index.to_string(),
304 vec![number(link.source), number(link.target)],
305 )
306}
307
308fn link_reply(link: Option<u32>) -> LinoDocument {
309 link.map(|link| vec![named("link", vec![number(link)])])
310 .unwrap_or_default()
311}
312
313fn change_side(link: &Link) -> LiNo<String> {
314 if link.is_null() {
315 group(Vec::new())
316 } else {
317 group(vec![link_lino(link)])
318 }
319}
320
321fn parse_change_side(side: &LiNo<String>) -> ProtocolResult<Link> {
322 match side {
323 LiNo::Link { id: None, values } if values.is_empty() => Ok(Link::null()),
324 LiNo::Link { id: None, values } if values.len() == 1 => parse_link(&values[0]),
325 link @ LiNo::Link { id: Some(_), .. } => parse_link(link),
328 _ => Err(ProtocolError::malformed(format!(
329 "expected a change side, found {}",
330 format_link(side)
331 ))),
332 }
333}
334
335fn parse_link(link: &LiNo<String>) -> ProtocolResult<Link> {
336 match link {
337 LiNo::Link {
338 id: Some(index),
339 values,
340 } => match values.as_slice() {
341 [source, target] => Ok(Link::new(
342 parse_text_number(index)?,
343 parse_number(source)?,
344 parse_number(target)?,
345 )),
346 _ => Err(ProtocolError::malformed(format!(
347 "expected (index: source target), found {}",
348 format_link(link)
349 ))),
350 },
351 _ => Err(ProtocolError::malformed(format!(
352 "expected (index: source target), found {}",
353 format_link(link)
354 ))),
355 }
356}
357
358fn restriction_lino(restriction: &[Part]) -> LiNo<String> {
359 group(
360 restriction
361 .iter()
362 .map(|part| part.map_or_else(|| LiNo::Ref(ANY.to_string()), number))
363 .collect(),
364 )
365}
366
367fn parse_restriction(argument: &LiNo<String>) -> ProtocolResult<Vec<Part>> {
368 let parts = parts(argument);
369 if parts.len() > 3 {
370 return Err(malformed_argument("restriction", argument));
371 }
372 parts
373 .iter()
374 .map(|part| match part {
375 LiNo::Ref(text) if text == ANY => Ok(None),
376 part => parse_number(part).map(Some),
377 })
378 .collect()
379}
380
381fn numbers_lino(numbers: &[u32]) -> LiNo<String> {
382 group(numbers.iter().copied().map(number).collect())
383}
384
385fn parse_numbers<const N: usize>(argument: &LiNo<String>) -> ProtocolResult<[u32; N]> {
386 let numbers = parts(argument)
387 .iter()
388 .map(parse_number)
389 .collect::<ProtocolResult<Vec<_>>>()?;
390 numbers
391 .try_into()
392 .map_err(|_| malformed_argument(&format!("{N} numbers"), argument))
393}
394
395fn parts(argument: &LiNo<String>) -> &[LiNo<String>] {
398 match argument {
399 LiNo::Link { id: None, values } => values,
400 reference => std::slice::from_ref(reference),
401 }
402}
403
404fn parse_number(value: &LiNo<String>) -> ProtocolResult<u32> {
405 match value {
406 LiNo::Ref(text) => parse_text_number(text),
407 _ => Err(ProtocolError::malformed(format!(
408 "expected a number, found {}",
409 format_link(value)
410 ))),
411 }
412}
413
414fn parse_text_number(text: &str) -> ProtocolResult<u32> {
415 text.parse()
416 .map_err(|_| ProtocolError::malformed(format!("expected a number, found '{text}'")))
417}
418
419fn number(value: u32) -> LiNo<String> {
420 LiNo::Ref(value.to_string())
421}
422
423fn named(id: &str, values: Vec<LiNo<String>>) -> LiNo<String> {
424 LiNo::Link {
425 id: Some(id.to_string()),
426 values,
427 }
428}
429
430fn group(values: Vec<LiNo<String>>) -> LiNo<String> {
431 LiNo::Link { id: None, values }
432}
433
434fn malformed_argument(expected: &str, argument: &LiNo<String>) -> ProtocolError {
435 ProtocolError::malformed(format!(
436 "expected {expected}, found {}",
437 format_link(argument)
438 ))
439}
440
441fn malformed_reply(expected: &str, document: &[LiNo<String>]) -> ProtocolError {
442 ProtocolError::malformed(format!(
443 "expected a {expected} reply, found {}",
444 format_document(document)
445 ))
446}