1use super::*;
2use crate::{audio::AudioId, card_provider::CardProvider, CardProperty, CardRefType};
3use either::Either;
4use ledgerstore::{ItemReference, Ledger, LedgerItem, LedgerType, PropertyCache};
5use omtrent::TimeStamp;
6use serde::{Deserialize, Serialize, Serializer};
7use std::{collections::HashSet, fmt::Display, str::FromStr};
8
9pub type CardId = Uuid;
10
11#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, Ord, PartialOrd)]
13pub struct TextData(Vec<Either<String, TextLink>>);
14
15impl From<String> for TextData {
16 fn from(value: String) -> Self {
17 Self::from_raw(&value)
18 }
19}
20
21impl TextData {
22 pub fn extend(&mut self, other: Self) {
23 for cmp in other.0 {
24 self.0.push(cmp);
25 }
26 }
27
28 pub fn push_link(&mut self, id: CardId, alias: Option<String>) {
29 let link = TextLink { id, alias };
30 self.0.push(Either::Right(link));
31 }
32
33 pub fn push_string(&mut self, s: String) {
34 self.0.push(Either::Left(s));
35 }
36
37 pub fn is_empty(&self) -> bool {
38 self.0.is_empty()
39 }
40
41 pub fn inner(&self) -> &Vec<Either<String, TextLink>> {
42 &self.0
43 }
44
45 pub fn inner_mut(&mut self) -> &mut Vec<Either<String, TextLink>> {
46 &mut self.0
47 }
48
49 pub fn evaluate(&self, provider: &CardProvider) -> String {
50 let mut out = String::new();
51
52 for cmp in &self.0 {
53 match cmp {
54 Either::Left(s) => out.push_str(&s),
55 Either::Right(TextLink { id, alias }) => match alias {
56 Some(alias) => out.push_str(&alias),
57 None => match provider.load(*id) {
58 Some(card) => out.push_str(&card.name),
59 None => out.push_str("<invalid card ref>"),
60 },
61 },
62 }
63 }
64
65 out
66 }
67
68 pub fn card_ids(&self) -> Vec<CardId> {
69 let mut out = vec![];
70
71 for cmp in &self.0 {
72 match cmp {
73 Either::Left(_) => {}
74 Either::Right(TextLink { id, .. }) => out.push(*id),
75 }
76 }
77
78 out
79 }
80
81 pub fn from_raw(input: &str) -> Self {
82 let mut result = Vec::new();
83 let mut buffer = String::new();
84 let mut chars = input.chars().peekable();
85
86 while let Some(c) = chars.next() {
87 if c == '[' && chars.peek() == Some(&'[') {
88 chars.next(); if !buffer.is_empty() {
92 result.push(Either::Left(std::mem::take(&mut buffer)));
93 }
94
95 let mut link_buf = String::new();
97 while let Some(ch) = chars.next() {
98 if ch == ']' && chars.peek() == Some(&']') {
99 chars.next(); break;
101 } else {
102 link_buf.push(ch);
103 }
104 }
105
106 let parts: Vec<&str> = link_buf.splitn(2, '|').collect();
107 let (id_str, alias_opt) = if parts.len() == 2 {
108 (parts[0], Some(parts[1].to_string()))
109 } else {
110 (parts[0], None)
111 };
112
113 match id_str.parse::<CardId>() {
114 Ok(id) => result.push(Either::Right(TextLink {
115 id,
116 alias: alias_opt,
117 })),
118 Err(_) => result.push(Either::Left(format!("[[{}]]", link_buf))),
119 }
120 } else {
121 buffer.push(c);
122 }
123 }
124
125 if !buffer.is_empty() {
126 result.push(Either::Left(buffer));
127 }
128
129 Self(result)
130 }
131
132 pub fn to_raw(&self) -> String {
133 let mut out = String::new();
134
135 for cmp in &self.0 {
136 let s = match cmp {
137 Either::Left(s) => s.to_string(),
138 Either::Right(TextLink { id, alias }) => match alias {
139 Some(alias) => format!("[[{id}|{alias}]]"),
140 None => format!("[[{id}]]"),
141 },
142 };
143
144 out.push_str(&s);
145 }
146
147 out
148 }
149}
150
151#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
152pub struct TextLink {
153 pub id: CardId,
154 pub alias: Option<String>,
155}
156
157impl TextLink {
158 pub fn new(id: CardId) -> Self {
159 Self { id, alias: None }
160 }
161}
162
163impl Serialize for TextData {
164 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
165 where
166 S: Serializer,
167 {
168 serializer.serialize_str(&self.to_raw())
169 }
170}
171
172impl<'de> Deserialize<'de> for TextData {
173 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
174 where
175 D: Deserializer<'de>,
176 {
177 let s = String::deserialize(deserializer)?;
178 Ok(TextData::from_raw(&s))
179 }
180}
181
182pub type AttributeId = Uuid;
183
184#[derive(PartialEq, Debug, Clone, Serialize, Hash, Eq, Ord, PartialOrd)]
265pub enum AttrBackType {
266 InstanceOfClass(CardId),
267 TimeStamp,
268 Boolean,
269}
270
271impl<'de> Deserialize<'de> for AttrBackType {
272 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
273 where
274 D: Deserializer<'de>,
275 {
276 let value = Value::deserialize(deserializer)?;
277
278 if let Ok(uuid) = Uuid::deserialize(&value) {
280 return Ok(AttrBackType::InstanceOfClass(uuid));
281 }
282
283 #[derive(Deserialize)]
285 enum Helper {
286 InstanceOfClass(CardId),
287 TimeStamp,
288 Boolean,
289 }
290
291 let helper: Helper = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
292 match helper {
293 Helper::InstanceOfClass(id) => Ok(AttrBackType::InstanceOfClass(id)),
294 Helper::TimeStamp => Ok(AttrBackType::TimeStamp),
295 Helper::Boolean => Ok(AttrBackType::Boolean),
296 }
297 }
298}
299
300#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialOrd, Ord)]
305pub struct Attrv2 {
306 pub id: AttributeId,
307 pub pattern: String,
308 pub back_type: Option<AttrBackType>,
309}
310
311pub enum BackSideConstraint {
312 Card { ty: Option<CardId> },
313}
314
315#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, Hash, Eq)]
316pub enum CardType {
317 Instance {
320 name: TextData,
321 #[serde(default, skip_serializing_if = "Option::is_none")]
322 back: Option<BackSide>,
323 class: CardId,
324 },
325 Normal {
326 front: TextData,
327 back: BackSide,
328 },
329 Unfinished {
330 front: TextData,
331 },
332 Attribute {
335 attribute: AttributeId,
336 back: BackSide,
337 instance: CardId,
338 },
339
340 Class {
343 name: TextData,
344 #[serde(default, skip_serializing_if = "Option::is_none")]
345 back: Option<BackSide>,
346 #[serde(default, skip_serializing_if = "Option::is_none")]
347 parent_class: Option<CardId>,
348 #[serde(default, skip_serializing_if = "Option::is_none")]
349 default_question: Option<TextData>,
350 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
351 attrs: BTreeSet<Attrv2>,
352 },
353
354 Statement {
370 front: TextData,
371 },
372
373 Event {
375 front: TextData,
376 start_time: TimeStamp,
377 end_time: Option<TimeStamp>,
378 parent_event: Option<CardId>,
379 },
380}
381
382impl CardType {
383 pub fn class(&self) -> Option<CardId> {
384 match self {
385 CardType::Instance { class, .. } => Some(*class),
386 CardType::Normal { .. } => None,
387 CardType::Unfinished { .. } => None,
388 CardType::Attribute { .. } => None,
389 CardType::Class { parent_class, .. } => *parent_class,
390 CardType::Statement { .. } => None,
391 CardType::Event { .. } => None,
392 }
393 }
394
395 pub fn backside(&self) -> Option<&BackSide> {
396 match self {
397 CardType::Instance {
398 back: Some(back), ..
399 } => {
400 if back.is_empty_text() {
401 None
402 } else {
403 Some(back)
404 }
405 }
406 CardType::Instance { back: None, .. } => None,
407 CardType::Normal { back, .. } => Some(back),
408 CardType::Unfinished { .. } => None,
409 CardType::Attribute { back, .. } if !back.is_empty_text() => Some(back),
410 CardType::Attribute { back, .. } => Some(back),
411 CardType::Class {
412 back: Some(back), ..
413 } if !back.is_empty_text() => Some(back),
414 CardType::Class {
415 back: Some(back), ..
416 } => Some(back),
417 CardType::Class { back: None, .. } => None,
418 CardType::Statement { .. } => None,
419 CardType::Event { .. } => None,
420 }
421 }
422
423 pub fn raw_front(&self) -> String {
424 match self.clone() {
425 CardType::Instance { name, .. } => name.to_raw(),
426 CardType::Normal { front, .. } => front.to_raw(),
427 CardType::Unfinished { front } => front.to_raw(),
428 CardType::Attribute { .. } => "attr card".to_string(),
429 CardType::Class { name, .. } => name.to_raw(),
430 CardType::Statement { front } => front.to_raw(),
431 CardType::Event { front, .. } => front.to_raw(),
432 }
433 }
434
435 pub fn raw_back(&self) -> String {
436 self.backside().map(|x| x.to_string()).unwrap_or_default()
437 }
438
439 pub async fn get_dependencies(&self) -> BTreeSet<CardId> {
440 match self {
441 CardType::Instance { class, back, .. } => {
442 let mut dependencies: BTreeSet<CardId> = Default::default();
443 dependencies.insert(*class);
444 dependencies.extend(
445 back.clone()
446 .map(|x| x.dependencies())
447 .unwrap_or_default()
448 .iter(),
449 );
450 dependencies
451 }
452 CardType::Normal { .. } => Default::default(),
453 CardType::Unfinished { .. } => Default::default(),
454 CardType::Attribute { back, instance, .. } => {
455 let mut dependencies: BTreeSet<CardId> = Default::default();
456 dependencies.insert(*instance);
457 dependencies.extend(back.dependencies().iter());
458 dependencies
459 }
460 CardType::Class {
461 back, parent_class, ..
462 } => {
463 let mut dependencies: BTreeSet<CardId> = Default::default();
464 dependencies.extend(back.as_ref().map(|x| x.dependencies()).unwrap_or_default());
465 if let Some(id) = parent_class {
466 dependencies.insert(*id);
467 }
468 dependencies
469 }
470 CardType::Statement { .. } => Default::default(),
471 CardType::Event { .. } => todo!(),
472 }
473 }
474
475 pub fn name_fixed_ledger(&self) -> TextData {
476 match self {
477 CardType::Instance { name, .. } => name.clone(),
478 CardType::Normal { front, .. } => front.clone(),
479 CardType::Unfinished { front, .. } => front.clone(),
480 CardType::Attribute { .. } => {
481 panic!()
482 }
483 CardType::Class { name, .. } => name.clone(),
484 CardType::Statement { front, .. } => front.clone(),
485 CardType::Event { front, .. } => front.clone(),
486 }
487 }
488
489 pub fn name(&self, provider: &CardProvider) -> TextData {
490 match self {
491 CardType::Instance { name, .. } => name.clone(),
492 CardType::Normal { front, .. } => front.clone(),
493 CardType::Unfinished { front, .. } => front.clone(),
494 CardType::Attribute {
495 attribute,
496 instance,
497 ..
498 } => {
499 let class: CardId = provider
500 .providers
501 .cards
502 .get_prop_cache(PropertyCache::new(
503 CardProperty::Attr,
504 attribute.to_string(),
505 ))
506 .into_iter()
507 .next()
508 .unwrap();
509
510 let class = provider.load(class).unwrap();
511 let attr = class.get_attr(*attribute).unwrap();
512 let instance = provider.load(*instance).unwrap().name_textdata();
513 let instance = instance.to_raw();
514
515 let new = attr.pattern.replace("{}", &instance);
516
517 TextData::from_raw(&new)
518 }
519 CardType::Class { name, .. } => name.clone(),
520 CardType::Statement { front, .. } => front.clone(),
521 CardType::Event { front, .. } => front.clone(),
522 }
523 }
524
525 pub fn display_front(&self, provider: &CardProvider) -> TextData {
526 match self {
527 CardType::Instance {
528 name, class, back, ..
529 } => {
530 let (class_name, default_question) =
531 match provider.providers.cards.load(*class).data {
532 CardType::Class {
533 default_question,
534 name,
535 ..
536 } => (name, default_question),
537 other => {
538 dbg!(class);
539 dbg!(other);
540 panic!();
541 }
542 };
543
544 let thename = &name.evaluate(provider);
545 let class_name = &class_name.evaluate(provider);
546
547 match default_question {
548 Some(q) => {
549 let s = q.evaluate(provider).replace("{}", thename);
550 TextData::from_raw(&s)
551 }
552
553 None => {
554 if back.is_some() {
555 let s = format!("{thename} ({class_name})");
556 TextData::from_raw(&s)
557 } else {
558 name.clone()
559 }
560 }
561 }
562 }
563 CardType::Normal { front, .. } => front.clone(),
564 CardType::Unfinished { front, .. } => front.clone(),
565 CardType::Attribute {
566 attribute,
567 instance,
568 ..
569 } => {
570 let class: CardId = provider
571 .providers
572 .cards
573 .get_prop_cache(PropertyCache::new(
574 CardProperty::Attr,
575 attribute.to_string(),
576 ))
577 .into_iter()
578 .next()
579 .unwrap();
580
581 let class = provider.load(class).unwrap();
582
583 let attr = class.get_attr(*attribute).unwrap();
584
585 let new = if attr.pattern.contains("{}") {
586 attr.pattern.replace("{}", &format!("[[{instance}]]"))
587 } else {
588 format!("[[{instance}]]: {}", attr.pattern)
589 };
590
591 TextData::from_raw(&new)
592 }
593 CardType::Class {
594 name, parent_class, ..
595 } => match parent_class {
596 Some(class) => {
597 let mut name = name.clone();
598
599 if self.backside().is_some() {
600 name.push_string(" ( ".to_string());
601 name.push_link(*class, None);
602 name.push_string(")".to_string());
603 }
604
605 name
606 }
607 None => name.clone(),
608 },
609 CardType::Statement { front, .. } => front.clone(),
610 CardType::Event { front, .. } => front.clone(),
611 }
612 }
613
614 pub fn type_name(&self) -> &str {
615 match self {
616 CardType::Unfinished { .. } => "unfinished",
617 CardType::Statement { .. } => "statement",
618 CardType::Attribute { .. } => "attribute",
619 CardType::Instance { .. } => "instance",
620 CardType::Normal { .. } => "normal",
621 CardType::Class { .. } => "class",
622 CardType::Event { .. } => "event",
623 }
624 }
625
626 pub fn fieldless(&self) -> CType {
628 match self {
629 CardType::Instance { .. } => CType::Instance,
630 CardType::Normal { .. } => CType::Normal,
631 CardType::Unfinished { .. } => CType::Unfinished,
632 CardType::Attribute { .. } => CType::Attribute,
633 CardType::Class { .. } => CType::Class,
634 CardType::Statement { .. } => CType::Statement,
635 CardType::Event { .. } => CType::Event,
636 }
637 }
638
639 pub fn parent_class(&self) -> Option<CardId> {
640 match self {
641 CardType::Class { parent_class, .. } => *parent_class,
642 _ => None,
643 }
644 }
645
646 pub fn is_class(&self) -> bool {
647 matches!(self, Self::Class { .. })
648 }
649 pub fn is_instance(&self) -> bool {
650 matches!(self, Self::Instance { .. })
651 }
652 pub fn is_finished(&self) -> bool {
653 !matches!(self, Self::Unfinished { .. })
654 }
655}
656
657fn bool_is_false(b: &bool) -> bool {
658 *b == false
659}
660
661#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
662pub struct RawCard {
663 pub id: Uuid,
664 #[serde(default, skip_serializing_if = "Option::is_none")]
667 pub namespace: Option<CardId>,
668 pub data: CardType,
669 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
670 pub explicit_dependencies: BTreeSet<Uuid>,
671 #[serde(default, skip_serializing_if = "bool_is_false")]
672 pub trivial: bool,
673 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
674 pub tags: BTreeMap<String, String>,
675 #[serde(default, skip_serializing_if = "Option::is_none")]
676 pub front_audio: Option<AudioId>,
677 #[serde(default, skip_serializing_if = "Option::is_none")]
678 pub back_audio: Option<AudioId>,
679}
680
681impl RawCard {
682 pub fn cache_front(&self, ledger: &Ledger<RawCard>) -> String {
683 match self.data.clone() {
684 CardType::Instance { name, .. } => name.to_raw(),
685 CardType::Normal { front, .. } => front.to_raw(),
686 CardType::Unfinished { front } => front.to_raw(),
687 CardType::Attribute {
688 attribute: _,
689 instance,
690 ..
691 } => {
692 dbg!(&self);
693 let attr = self.get_attr_rec(ledger.to_owned()).unwrap();
694
695 let instance = ledger.load(instance).data.name_fixed_ledger();
696 let instance = instance.to_raw();
697
698 let new = attr.pattern.replace("{}", &instance);
699 new
700 }
701 CardType::Class { name, .. } => name.to_raw(),
702 CardType::Statement { front } => front.to_raw(),
703 CardType::Event { front, .. } => front.to_raw(),
704 }
705 }
706
707 pub fn parent_class(&self) -> Option<CardId> {
709 match self.data {
710 CardType::Instance { class, .. } => Some(class),
711 CardType::Normal { .. } => None,
712 CardType::Unfinished { .. } => None,
713 CardType::Attribute { .. } => None,
714 CardType::Class { parent_class, .. } => parent_class,
715 CardType::Statement { .. } => None,
716 CardType::Event { .. } => None,
717 }
718 }
719
720 pub fn ref_mut_backside(&mut self) -> Option<&mut BackSide> {
721 match &mut self.data {
722 CardType::Instance { back, .. } => back.as_mut(),
723 CardType::Normal { back, .. } => Some(back),
724 CardType::Unfinished { .. } => None,
725 CardType::Attribute { back, .. } => Some(back),
726 CardType::Class { back, .. } => back.as_mut(),
727 CardType::Statement { .. } => None,
728 CardType::Event { .. } => None,
729 }
730 }
731
732 pub fn ref_backside(&self) -> Option<&BackSide> {
733 match &self.data {
734 CardType::Instance { back, .. } => back.as_ref(),
735 CardType::Normal { back, .. } => Some(back),
736 CardType::Unfinished { .. } => None,
737 CardType::Attribute { back, .. } => Some(back),
738 CardType::Class { back, .. } => back.as_ref(),
739 CardType::Statement { .. } => None,
740 CardType::Event { .. } => None,
741 }
742 }
743
744 fn attrs(&self) -> BTreeSet<Attrv2> {
745 if let CardType::Class { ref attrs, .. } = &self.data {
746 return attrs.clone();
747 } else {
748 Default::default()
749 }
750 }
751
752 pub fn get_attr(&self, id: AttributeId) -> Option<Attrv2> {
753 if let CardType::Class { ref attrs, .. } = &self.data {
754 attrs.iter().find(|attr| attr.id == id).cloned()
755 } else {
756 None
757 }
758 }
759
760 pub fn get_attr_rec(&self, ledger: Ledger<RawCard>) -> Option<Attrv2> {
761 let CardType::Attribute {
762 attribute,
763 instance,
764 ..
765 } = &self.data
766 else {
767 return None;
768 };
769
770 let mut card: Self = ledger.load(*instance);
771
772 while let Some(parent) = card.parent_class() {
773 card = ledger.load(parent);
774 if let Some(attr) = card.get_attr(*attribute) {
775 return Some(attr);
776 }
777 }
778
779 None
780 }
781
782 pub fn set_backside(mut self, new_back: BackSide) -> Self {
783 let data = match self.data.clone() {
784 x @ CardType::Event { .. } => x,
785 CardType::Instance {
786 name,
787 back: _,
788 class,
789 } => CardType::Instance {
790 name,
791 back: Some(new_back),
792 class,
793 },
794 x @ CardType::Statement { .. } => x,
795
796 CardType::Normal { front, back: _ } => CardType::Normal {
797 front,
798 back: new_back,
799 },
800 CardType::Unfinished { front } => CardType::Normal {
801 front,
802 back: new_back,
803 },
804 CardType::Attribute {
805 attribute,
806 instance: concept_card,
807 back: _,
808 } => CardType::Attribute {
809 attribute,
810 back: new_back,
811 instance: concept_card,
812 },
813 CardType::Class {
814 name,
815 back: _,
816 parent_class,
817 default_question,
818 attrs,
819 } => CardType::Class {
820 name,
821 back: Some(new_back),
822 parent_class,
823 default_question,
824 attrs,
825 },
826 };
827
828 self.data = data;
829 self
830 }
831}
832
833pub fn bigrams(text: &str) -> Vec<[char; 2]> {
834 normalize_string(text)
835 .chars()
836 .collect::<Vec<_>>()
837 .windows(2)
838 .map(|w| [w[0], w[1]])
839 .collect()
840}
841
842pub fn normalize_string(str: &str) -> String {
843 deunicode::deunicode(str)
844 .to_lowercase()
845 .chars()
846 .filter(|c| c.is_ascii_alphanumeric())
847 .collect()
848}
849
850use fancy_regex::Regex;
851fn resolve_text(txt: String, ledger: &Ledger<RawCard>, re: &Regex) -> String {
852 let uuids: Vec<CardId> = re
853 .find_iter(&txt)
854 .filter_map(Result::ok)
855 .map(|m| m.as_str().parse().unwrap())
856 .collect();
857
858 let mut s: String = re.replace_all(&txt, "").to_string();
859 for id in uuids {
860 let card = ledger.load(id);
861 let txt = card.cache_front(ledger);
862 s.push_str(&resolve_text(txt, ledger, re));
863 }
864
865 s
866}
867
868fn resolve_card(card: &RawCard, ledger: &Ledger<RawCard>) -> String {
871 let uuid_regex = Regex::new(
872 r"\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b",
873 )
874 .unwrap();
875
876 resolve_text(card.cache_front(ledger), ledger, &uuid_regex)
877}
878
879#[derive(Debug)]
880pub enum CardError {
881 InstanceOfNonClass,
882 AttributeOfNonInstance,
883 MissingAttribute,
884 DefaultQuestionNotClass,
885 WrongCardType,
886 AnswerMustBeCard,
887 AnswerMustBeTime,
888 AnswerMustBeBool,
889 SubClassOfNonClass,
890 BackTypeMustBeClass,
891}
892
893fn instance_is_of_type(instance: CardId, ty: CardId, ledger: &LedgerType<RawCard>) -> bool {
894 let instance = ledger.load(instance);
895 assert!(instance.data.is_instance());
896
897 get_parent_classes(ty, ledger)
898 .into_iter()
899 .find(|class| class.id == ty)
900 .is_some()
901}
902
903fn get_parent_classes(class: CardId, ledger: &LedgerType<RawCard>) -> Vec<RawCard> {
904 let class = ledger.load(class);
905 let mut classes: Vec<RawCard> = vec![class.clone()];
906 assert!(class.data.is_class());
907 let mut parent_class = class.parent_class();
908
909 while let Some(parent) = parent_class {
910 let class = ledger.load(parent);
911 assert!(class.data.is_class());
912 parent_class = class.parent_class();
913 classes.push(class);
914 }
915
916 classes
917}
918
919fn get_attributes(class: CardId, ledger: &LedgerType<RawCard>) -> Vec<Attrv2> {
920 let mut out: Vec<Attrv2> = vec![];
921 for class in get_parent_classes(class, ledger) {
922 if let CardType::Class { attrs, .. } = class.data {
923 out.extend(attrs);
924 } else {
925 panic!()
926 }
927 }
928 out
929}
930
931impl LedgerItem for RawCard {
932 type Error = CardError;
933 type Key = CardId;
934 type RefType = CardRefType;
935 type PropertyType = CardProperty;
936 type Modifier = CardAction;
937
938 fn validate(&self, ledger: &LedgerType<Self>) -> Result<(), Self::Error> {
939 match &self.data {
940 CardType::Instance {
941 name: _,
942 back: _,
943 class,
944 } => {
945 if !ledger.load(*class).data.is_class() {
946 return Err(CardError::InstanceOfNonClass);
947 }
948 }
949
950 CardType::Normal { front: _, back: _ } => {}
951 CardType::Unfinished { front: _ } => {}
952 CardType::Attribute {
953 attribute,
954 back: attr_back,
955 instance,
956 } => {
957 let CardType::Instance { name, back, class } = ledger.load(*instance).data else {
958 return Err(CardError::InstanceOfNonClass);
959 };
960
961 let class = {
962 let class = ledger.load(class);
963 if !class.data.is_class() {
964 return Err(CardError::InstanceOfNonClass);
965 }
966 class
967 };
968
969 let Some(Attrv2 { back_type, .. }) = get_attributes(class.id, &ledger)
970 .into_iter()
971 .find(|attr| attr.id == *attribute)
972 else {
973 return Err(CardError::MissingAttribute);
974 };
975
976 match back_type {
977 Some(AttrBackType::Boolean) => {
978 if !matches!(attr_back, BackSide::Bool(_)) {
979 return Err(CardError::AnswerMustBeBool);
980 }
981 }
982 Some(AttrBackType::TimeStamp) => {
983 if !matches!(attr_back, BackSide::Time(_)) {
984 return Err(CardError::AnswerMustBeTime);
985 }
986 }
987 Some(AttrBackType::InstanceOfClass(back_class)) => {
988 if let BackSide::Card(answer) = attr_back {
989 if !instance_is_of_type(*answer, back_class, &ledger) {
990 return Err(CardError::WrongCardType);
991 }
992 } else {
993 dbg!(name);
994 dbg!(&back);
995 dbg!(&attr_back);
996 dbg!(ledger.load(*instance));
997 dbg!(self);
998 let err = dbg!(CardError::AnswerMustBeCard);
999 return Err(err);
1000 }
1001 }
1002 None => {}
1003 }
1004 }
1005 CardType::Class {
1006 name: _,
1007 back: _,
1008 parent_class,
1009 default_question: _,
1010 attrs,
1011 } => {
1012 if let Some(parent) = parent_class {
1013 if !ledger.load(*parent).data.is_class() {
1014 return Err(CardError::SubClassOfNonClass);
1015 }
1016 }
1017
1018 for attr in attrs {
1019 if let Some(AttrBackType::InstanceOfClass(back_type)) = attr.back_type {
1020 if !ledger.load(back_type).data.is_class() {
1021 return Err(CardError::BackTypeMustBeClass);
1022 }
1023 }
1024 }
1025 }
1026 CardType::Statement { front: _ } => {}
1027 CardType::Event {
1029 front: _,
1030 start_time: _,
1031 end_time: _,
1032 parent_event: _,
1033 } => {}
1034 }
1035
1036 Ok(())
1037 }
1038
1039 fn ref_cache(&self) -> HashSet<ItemReference<Self>> {
1040 let from = self.id;
1041 let mut out: HashSet<ItemReference<Self>> = Default::default();
1042
1043 if let Some(ns) = self.namespace {
1044 out.insert(ItemReference::new(from, ns, CardRefType::LinkRef));
1045 }
1046
1047 for dep in &self.explicit_dependencies {
1048 out.insert(ItemReference::new(
1049 from,
1050 *dep,
1051 CardRefType::ExplicitDependency,
1052 ));
1053 }
1054
1055 match &self.data {
1056 CardType::Normal { front, .. } => {
1057 for id in front.card_ids() {
1058 out.insert(ItemReference::new(from, id, CardRefType::LinkRef));
1059 }
1060 }
1061 CardType::Unfinished { front, .. } => {
1062 for id in front.card_ids() {
1063 out.insert(ItemReference::new(from, id, CardRefType::LinkRef));
1064 }
1065 }
1066 CardType::Instance { name, class, .. } => {
1067 for id in name.card_ids() {
1068 out.insert(ItemReference::new(from, id, CardRefType::LinkRef));
1069 }
1070
1071 out.insert(ItemReference::new(
1072 from,
1073 *class,
1074 CardRefType::ClassOfInstance,
1075 ));
1076 }
1077 CardType::Attribute { instance, .. } => {
1078 out.insert(ItemReference::new(
1079 from,
1080 *instance,
1081 CardRefType::InstanceOfAttribute,
1082 ));
1083 }
1084 CardType::Class {
1085 name,
1086 default_question,
1087 parent_class,
1088 ..
1089 } => {
1090 for id in name.card_ids() {
1091 out.insert(ItemReference::new(from, id, CardRefType::LinkRef));
1092 }
1093
1094 if let Some(def) = default_question {
1095 for id in def.card_ids() {
1096 out.insert(ItemReference::new(from, id, CardRefType::LinkRef));
1097 }
1098 }
1099
1100 if let Some(class) = parent_class {
1101 out.insert(ItemReference::new(from, *class, CardRefType::ParentClass));
1102 }
1103 }
1104 CardType::Statement { front, .. } => {
1105 for id in front.card_ids() {
1106 out.insert(ItemReference::new(from, id, CardRefType::LinkRef));
1107 }
1108 }
1109 CardType::Event { front, .. } => {
1110 for id in front.card_ids() {
1111 out.insert(ItemReference::new(from, id, CardRefType::LinkRef));
1112 }
1113 }
1114 };
1115
1116 if let Some(back) = &self.data.backside() {
1117 match back {
1118 BackSide::Text(txt) => {
1119 for id in txt.card_ids() {
1120 out.insert(ItemReference::new(from, id, CardRefType::LinkRef));
1121 }
1122 }
1123 BackSide::Card(id) => {
1124 out.insert(ItemReference::new(from, *id, CardRefType::LinkRef));
1125 }
1126 BackSide::List(ids) => {
1127 for id in ids {
1128 out.insert(ItemReference::new(from, *id, CardRefType::LinkRef));
1129 }
1130 }
1131 BackSide::Time(_) => {}
1132 BackSide::Trivial => {}
1133 BackSide::Invalid => {}
1134 BackSide::Bool(_) => {}
1135 }
1136 }
1137
1138 out
1139 }
1140
1141 fn properties_cache(&self, cache: &Ledger<Self>) -> HashSet<PropertyCache<Self>> {
1142 let mut out: HashSet<PropertyCache<Self>> = Default::default();
1143
1144 let resolved_text = resolve_card(self, &cache);
1145
1146 for bigram in bigrams(&resolved_text) {
1147 let value = format!("{}{}", bigram[0], bigram[1]);
1148 let prop = PropertyCache {
1149 property: CardProperty::Bigram,
1150 value,
1151 };
1152 out.insert(prop);
1153 }
1154
1155 if self.trivial {
1156 out.insert(PropertyCache {
1157 property: CardProperty::Trivial,
1158 value: self.trivial.to_string(),
1159 });
1160 }
1161
1162 match &self.data {
1163 CardType::Normal { .. } => {}
1164 CardType::Unfinished { .. } => {}
1165 CardType::Instance { .. } => {}
1166 CardType::Attribute { attribute, .. } => {
1167 let prop = PropertyCache {
1168 property: CardProperty::AttrId,
1169 value: attribute.to_string(),
1170 };
1171 out.insert(prop);
1172 }
1173 CardType::Class { attrs, .. } => {
1174 for attr in attrs {
1175 let prop = PropertyCache {
1176 property: CardProperty::Attr,
1177 value: attr.id.to_string(),
1178 };
1179 out.insert(prop);
1180 }
1181 }
1182 CardType::Statement { .. } => {}
1183 CardType::Event { .. } => {}
1184 };
1185
1186 let val = format!("{:?}", self.data.fieldless());
1187 let prop = PropertyCache {
1188 property: CardProperty::CardType,
1189 value: val,
1190 };
1191
1192 out.insert(prop);
1193
1194 out
1195 }
1196
1197 fn new_default(id: CardId) -> Self {
1198 Self {
1199 id,
1200 namespace: None,
1201 data: CardType::Unfinished {
1202 front: TextData::from_raw("uninit"),
1203 },
1204 trivial: false,
1205 tags: Default::default(),
1206 explicit_dependencies: Default::default(),
1207 front_audio: Default::default(),
1208 back_audio: Default::default(),
1209 }
1210 }
1211
1212 fn inner_run_event(mut self, event: CardAction) -> Result<Self, Self::Error> {
1213 match event {
1214 CardAction::SetDefaultQuestion(default) => match &mut self.data {
1215 CardType::Class {
1216 ref mut default_question,
1217 ..
1218 } => *default_question = default.map(|s| TextData::from_raw(&s)),
1219 _ => return Err(CardError::DefaultQuestionNotClass),
1220 },
1221 CardAction::SetBackTime(ts) => {
1222 self = self.set_backside(BackSide::Time(ts));
1223 }
1224 CardAction::SetTrivial(flag) => {
1225 self.trivial = flag;
1226 }
1227 CardAction::SetBackBool(b) => {
1228 let backside = BackSide::Bool(b);
1229 self = self.set_backside(backside);
1230 }
1231 CardAction::SetFrontAudio(audio) => {
1232 self.front_audio = audio;
1233 }
1234 CardAction::SetBackAudio(audio) => {
1235 self.back_audio = audio;
1236 }
1237 CardAction::UpsertCard(ty) => {
1238 self.data = ty;
1239 }
1240 CardAction::SetNamespace(ns) => {
1241 self.namespace = ns;
1242 }
1243 CardAction::AddDependency(dependency) => {
1244 self.explicit_dependencies.insert(dependency);
1245 }
1246 CardAction::RemoveDependency(dependency) => {
1247 self.explicit_dependencies.remove(&dependency);
1248 }
1249 CardAction::SetBackRef(reff) => {
1250 let backside = BackSide::Card(reff);
1251 self = self.set_backside(backside);
1252 }
1253 CardAction::InsertAttr(attrv2) => {
1254 if let CardType::Class { ref mut attrs, .. } = self.data {
1255 attrs.insert(attrv2);
1256 } else {
1257 panic!("expeted class");
1258 }
1259 }
1260 CardAction::RemoveAttr(attr_id) => {
1261 if let CardType::Class { ref mut attrs, .. } = self.data {
1262 dbg!(&attrs);
1263 let attr_len = attrs.len();
1264 attrs.retain(|attr| attr.id != attr_id);
1265 assert_eq!(attr_len - 1, attrs.len());
1266 dbg!(&attrs);
1267 } else {
1268 panic!("expeted class");
1269 }
1270 }
1271 CardAction::SetParentClass(new_parent_class) => {
1272 if let CardType::Class {
1273 ref mut parent_class,
1274 ..
1275 } = self.data
1276 {
1277 *parent_class = new_parent_class;
1278 } else {
1279 panic!("expeted class");
1280 }
1281 }
1282 CardAction::SetInstanceClass(instance_class) => {
1283 if let CardType::Instance { ref mut class, .. } = self.data {
1284 *class = instance_class;
1285 } else {
1286 panic!("expected instance");
1287 }
1288 }
1289 CardAction::AttributeType {
1290 attribute,
1291 back,
1292 instance,
1293 } => {
1294 self.data = CardType::Attribute {
1295 attribute,
1296 back,
1297 instance,
1298 };
1299 }
1300 CardAction::NormalType { front, back } => {
1301 self.data = CardType::Normal { front, back };
1302 }
1303 CardAction::InstanceType { front, class } => {
1304 let back = self.ref_backside().cloned();
1305 self.data = CardType::Instance {
1306 name: front,
1307 back,
1308 class,
1309 };
1310 }
1311 CardAction::StatementType { front } => {
1312 self.data = CardType::Statement { front };
1313 }
1314 CardAction::ClassType { front } => {
1315 self.data = CardType::Class {
1316 name: front,
1317 back: self.ref_backside().cloned(),
1318 parent_class: self.parent_class(),
1319 default_question: None,
1320 attrs: self.attrs(),
1321 };
1322 }
1323 CardAction::UnfinishedType { front } => {
1324 self.data = CardType::Unfinished { front };
1325 }
1326 CardAction::EventType { front, start_time } => {
1327 self.data = CardType::Event {
1328 front,
1329 start_time,
1330 end_time: None,
1331 parent_event: None,
1332 };
1333 }
1334 CardAction::SetBackside(back_side) => match &mut self.data {
1335 CardType::Instance { ref mut back, .. } => {
1336 *back = back_side;
1337 }
1338 CardType::Normal { ref mut back, .. } => {
1339 if let Some(back_side) = back_side {
1340 *back = back_side;
1341 } else {
1342 panic!("normal cards require backside");
1343 }
1344 }
1345 CardType::Unfinished { .. } => {
1346 panic!("nope, unfinishde");
1347 }
1348 CardType::Attribute { ref mut back, .. } => {
1349 if let Some(back_side) = back_side {
1350 *back = back_side;
1351 } else {
1352 panic!("attr cards require backside");
1353 }
1354 }
1355 CardType::Class { ref mut back, .. } => {
1356 *back = back_side;
1357 }
1358 CardType::Statement { .. } => panic!("no back on statement"),
1359 CardType::Event { .. } => panic!("no back on event"),
1360 },
1361 CardAction::InsertAttrs(new_attrs) => {
1362 if let CardType::Class { ref mut attrs, .. } = self.data {
1363 *attrs = new_attrs;
1364 } else {
1365 panic!("expected class");
1366 }
1367 }
1368 CardAction::SetFront(new_front) => match &mut self.data {
1369 CardType::Instance { ref mut name, .. } => {
1370 *name = new_front;
1371 }
1372 CardType::Normal { front, .. } => {
1373 *front = new_front;
1374 }
1375 CardType::Unfinished { front } => {
1376 *front = new_front;
1377 }
1378 CardType::Attribute { .. } => {
1379 panic!("cant set frontside on attr cards")
1380 }
1381 CardType::Class { name, .. } => {
1382 *name = new_front;
1383 }
1384 CardType::Statement { front } => *front = new_front,
1385 CardType::Event { front, .. } => {
1386 *front = new_front;
1387 }
1388 },
1389 };
1390
1391 let implicit_deps: BTreeSet<Uuid> = {
1392 let mut all = self.ref_cache();
1393 all.retain(|ItemReference { ty, .. }| match ty {
1394 CardRefType::ExplicitDependency => false,
1395 _ => true,
1396 });
1397
1398 all.into_iter().map(|x| x.to).collect()
1399 };
1400
1401 self.explicit_dependencies = self
1402 .explicit_dependencies
1403 .difference(&implicit_deps)
1404 .cloned()
1405 .collect();
1406
1407 Ok(self)
1408 }
1409
1410 fn item_id(&self) -> CardId {
1411 self.id
1412 }
1413}
1414
1415#[derive(Serialize, Ord, PartialOrd, Eq, Hash, PartialEq, Debug, Clone)]
1416pub enum BackSide {
1417 Bool(bool),
1418 Text(TextData),
1419 Card(CardId),
1420 List(Vec<CardId>),
1421 Time(TimeStamp),
1422 Trivial, Invalid, }
1425
1426#[derive(Serialize, Deserialize, Ord, PartialOrd, Eq, Hash, PartialEq, Debug, Clone)]
1427pub enum BarSide {
1428 Bool(bool),
1429 Text(String),
1430 Card(CardId),
1431 List(Vec<CardId>),
1432 Time(TimeStamp),
1433 Trivial,
1434 Invalid,
1435}
1436
1437impl From<BarSide> for BackSide {
1438 fn from(value: BarSide) -> Self {
1439 match value {
1440 BarSide::Text(val) => BackSide::Text(val.into()),
1441 BarSide::Bool(val) => BackSide::Bool(val),
1442 BarSide::Card(val) => BackSide::Card(val),
1443 BarSide::List(val) => BackSide::List(val),
1444 BarSide::Time(val) => BackSide::Time(val),
1445 BarSide::Trivial => BackSide::Trivial,
1446 BarSide::Invalid => BackSide::Invalid,
1447 }
1448 }
1449}
1450
1451impl Default for BackSide {
1452 fn default() -> Self {
1453 Self::Text(Default::default())
1454 }
1455}
1456
1457impl From<String> for BackSide {
1458 fn from(s: String) -> Self {
1459 if let Ok(uuid) = Uuid::parse_str(&s) {
1460 Self::Card(uuid)
1461 } else if let Ok(timestamp) = TimeStamp::from_str(&s) {
1462 Self::Time(timestamp)
1463 } else if s.as_str() == Self::INVALID_STR {
1464 Self::Invalid
1465 } else {
1466 Self::Text(s.into())
1467 }
1468 }
1469}
1470
1471impl BackSide {
1472 pub const INVALID_STR: &'static str = "__INVALID__";
1473
1474 pub fn is_empty_text(&self) -> bool {
1475 if let Self::Text(s) = self {
1476 s.is_empty()
1477 } else {
1478 false
1479 }
1480 }
1481
1482 pub fn is_time(&self) -> bool {
1483 matches!(self, Self::Time(_))
1484 }
1485
1486 pub fn is_text(&self) -> bool {
1487 matches!(self, Self::Text(_))
1488 }
1489
1490 pub fn is_ref(&self) -> bool {
1491 matches!(self, Self::Card(_))
1492 }
1493
1494 pub fn as_timestamp(&self) -> Option<TimeStamp> {
1495 if let Self::Time(ts) = self {
1496 Some(ts.to_owned())
1497 } else {
1498 None
1499 }
1500 }
1501
1502 pub fn as_bool(&self) -> Option<bool> {
1503 if let Self::Bool(b) = self {
1504 Some(*b)
1505 } else {
1506 None
1507 }
1508 }
1509
1510 pub fn as_card(&self) -> Option<CardId> {
1511 if let Self::Card(card) = self {
1512 Some(*card)
1513 } else {
1514 None
1515 }
1516 }
1517
1518 pub fn to_string(&self) -> String {
1519 match self {
1520 BackSide::Bool(b) => b.to_string(),
1521 BackSide::Text(s) => s.to_raw(),
1522 BackSide::Card(id) => id.to_string(),
1523 BackSide::List(ids) => format!("{ids:?}"),
1524 BackSide::Time(ts) => dbg!(ts.serialize()),
1525 BackSide::Trivial => "<trivial>".to_string(),
1526 BackSide::Invalid => "<invalid>".to_string(),
1527 }
1528 }
1529
1530 pub fn dependencies(&self) -> BTreeSet<CardId> {
1531 let mut set = BTreeSet::default();
1532 match self {
1533 BackSide::Text(s) => {
1534 set.extend(s.card_ids());
1535 }
1536 BackSide::Card(card_id) => {
1537 let _ = set.insert(*card_id);
1538 }
1539 BackSide::List(vec) => {
1540 set.extend(vec.iter());
1541 }
1542 BackSide::Time(_) => {}
1543 BackSide::Trivial => {}
1544 BackSide::Invalid => {}
1545 BackSide::Bool(_) => {}
1546 }
1547
1548 set
1549 }
1550}
1551
1552impl<'de> Deserialize<'de> for BackSide {
1553 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1554 where
1555 D: Deserializer<'de>,
1556 {
1557 let value = Value::deserialize(deserializer)?;
1558
1559 match value {
1560 Value::Array(arr) => {
1561 let mut ids = Vec::new();
1562 for item in arr {
1563 if let Value::String(ref s) = item {
1564 if let Ok(uuid) = Uuid::parse_str(s) {
1565 ids.push(uuid);
1566 } else {
1567 return Err(serde::de::Error::custom("Invalid UUID in array"));
1568 }
1569 } else {
1570 return Err(serde::de::Error::custom("Expected string in array"));
1571 }
1572 }
1573 Ok(BackSide::List(ids))
1574 }
1575 Value::Bool(_) => Ok(BackSide::Trivial),
1576 Value::String(s) => Ok(s.into()),
1577 val => Ok(serde_json::from_value::<BarSide>(val).unwrap().into()),
1578 }
1579 }
1580}
1581
1582#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1583pub struct Config;
1584
1585#[derive(
1586 Serialize, Deserialize, Debug, Clone, Default, Copy, Eq, PartialEq, Hash, PartialOrd, Ord,
1587)]
1588#[serde(rename_all = "lowercase")]
1589pub enum CType {
1590 Instance,
1591 #[default]
1592 Normal,
1593 Unfinished,
1594 Attribute,
1595 Class,
1596 Statement,
1597 Event,
1598}
1599
1600impl Display for CType {
1601 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1602 write!(f, "{:?}", self)
1603 }
1604}
1605
1606impl CType {
1607 pub fn short_form(&self) -> &'static str {
1608 match self {
1609 CType::Instance => "I",
1610 CType::Normal => "N",
1611 CType::Attribute => "A",
1612 CType::Class => "C",
1613 CType::Unfinished => "U",
1614 CType::Statement => "S",
1615 CType::Event => "E",
1616 }
1617 }
1618}