qlib_rs/data/
store.rs

1use serde::{Deserialize, Serialize};
2use std::{collections::HashMap, error, mem::discriminant, sync::Arc};
3
4use crate::{
5    data::{
6        entity_schema::Complete, now, request::PushCondition, EntityType, FieldType, Timestamp
7    },
8    sadd, sread, sref, sreflist, sstr, ssub, swrite, AdjustBehavior, Entity, EntityId,
9    EntitySchema, Field, FieldSchema, Request, Result, Single, Snowflake, Value,
10};
11
12#[derive(Debug, Clone)]
13pub struct EntityExists(EntityId);
14impl error::Error for EntityExists {}
15impl std::fmt::Display for EntityExists {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        write!(f, "Entity already exists: {}", self.0)
18    }
19}
20
21#[derive(Debug, Clone)]
22pub struct EntityTypeNotFound(EntityType);
23impl error::Error for EntityTypeNotFound {}
24impl std::fmt::Display for EntityTypeNotFound {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(f, "Unknown entity type: {}", self.0)
27    }
28}
29
30#[derive(Debug, Clone)]
31pub struct EntityNotFound(EntityId);
32impl error::Error for EntityNotFound {}
33impl std::fmt::Display for EntityNotFound {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        write!(f, "Entity not found: {}", self.0)
36    }
37}
38
39#[derive(Debug, Clone)]
40pub struct FieldNotFound(EntityId, FieldType);
41impl error::Error for FieldNotFound {}
42impl std::fmt::Display for FieldNotFound {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        write!(f, "Field not found for entity {}: {}", self.0, self.1)
45    }
46}
47
48#[derive(Debug, Clone)]
49pub struct ValueTypeMismatch(EntityId, FieldType, Value, Value);
50impl error::Error for ValueTypeMismatch {}
51impl std::fmt::Display for ValueTypeMismatch {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        write!(
54            f,
55            "Value type mismatch for entity {} field {}: expected {:?}, got {:?}",
56            self.0, self.1, self.2, self.3
57        )
58    }
59}
60
61#[derive(Debug, Clone)]
62pub struct UnsupportAdjustBehavior(EntityId, FieldType, AdjustBehavior);
63impl error::Error for UnsupportAdjustBehavior {}
64impl std::fmt::Display for UnsupportAdjustBehavior {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        write!(
67            f,
68            "Unsupported adjust behavior for entity {} field {}: {:?}",
69            self.0, self.1, self.2
70        )
71    }
72}
73
74#[derive(Debug, Clone)]
75pub enum BadIndirectionReason {
76    NegativeIndex(i64),
77    ArrayIndexOutOfBounds(usize, usize),
78    EmptyEntityReference,
79    InvalidEntityId(EntityId),
80    UnexpectedValueType(FieldType, String),
81    ExpectedIndexAfterEntityList(FieldType),
82    FailedToResolveField(FieldType, String),
83}
84
85#[derive(Debug, Clone)]
86pub struct BadIndirection {
87    entity_id: EntityId,
88    field_type: FieldType,
89    reason: BadIndirectionReason,
90}
91
92impl BadIndirection {
93    pub fn new(entity_id: EntityId, field_type: FieldType, reason: BadIndirectionReason) -> Self {
94        BadIndirection {
95            entity_id,
96            field_type,
97            reason,
98        }
99    }
100}
101
102impl error::Error for BadIndirection {}
103
104impl std::fmt::Display for BadIndirection {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        write!(
107            f,
108            "Bad indirection for entity {}, field {}: ",
109            self.entity_id, self.field_type
110        )?;
111        match &self.reason {
112            BadIndirectionReason::NegativeIndex(index) => {
113                write!(f, "negative index: {}", index)
114            }
115            BadIndirectionReason::ArrayIndexOutOfBounds(index, size) => {
116                write!(f, "array index out of bounds: {} >= {}", index, size)
117            }
118            BadIndirectionReason::EmptyEntityReference => {
119                write!(f, "empty entity reference")
120            }
121            BadIndirectionReason::InvalidEntityId(id) => {
122                write!(f, "invalid entity id: {}", id)
123            }
124            BadIndirectionReason::UnexpectedValueType(field, value) => {
125                write!(f, "unexpected value type for field {}: {}", field, value)
126            }
127            BadIndirectionReason::ExpectedIndexAfterEntityList(field) => {
128                write!(f, "expected index after EntityList, got: {}", field)
129            }
130            BadIndirectionReason::FailedToResolveField(field, error) => {
131                write!(f, "failed to resolve field {}: {}", field, error)
132            }
133        }
134    }
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct Context {}
139
140/// Represents a complete snapshot of the store at a point in time
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct Snapshot {
143    schemas: HashMap<EntityType, EntitySchema<Single>>,
144    entities: HashMap<EntityType, Vec<EntityId>>,
145    types: Vec<EntityType>,
146    fields: HashMap<EntityId, HashMap<FieldType, Field>>,
147}
148
149/// Pagination options for retrieving lists of items
150#[derive(Debug, Clone)]
151pub struct PageOpts {
152    /// The maximum number of items to return
153    pub limit: usize,
154    /// The starting point for pagination
155    pub cursor: Option<String>,
156}
157
158impl Default for PageOpts {
159    fn default() -> Self {
160        PageOpts {
161            limit: 100,
162            cursor: None,
163        }
164    }
165}
166
167impl PageOpts {
168    pub fn new(limit: usize, cursor: Option<String>) -> Self {
169        PageOpts { limit, cursor }
170    }
171}
172
173/// Result of a paginated query
174#[derive(Debug, Clone)]
175pub struct PageResult<T> {
176    /// The items returned in this page
177    pub items: Vec<T>,
178    /// The total number of items available
179    pub total: usize,
180    /// Cursor for retrieving the next page, if available
181    pub next_cursor: Option<String>,
182}
183
184impl<T> PageResult<T> {
185    pub fn new(items: Vec<T>, total: usize, next_cursor: Option<String>) -> Self {
186        PageResult {
187            items,
188            total,
189            next_cursor,
190        }
191    }
192}
193
194pub struct Store {
195    schemas: HashMap<EntityType, EntitySchema<Single>>,
196    entities: HashMap<EntityType, Vec<EntityId>>,
197    types: Vec<EntityType>,
198    fields: HashMap<EntityId, HashMap<FieldType, Field>>,
199    snowflake: Arc<Snowflake>,
200}
201
202impl Store {
203    pub fn new(snowflake: Arc<Snowflake>) -> Self {
204        Store {
205            schemas: HashMap::new(),
206            entities: HashMap::new(),
207            types: Vec::new(),
208            fields: HashMap::new(),
209            snowflake,
210        }
211    }
212
213    pub fn create_entity(
214        &mut self,
215        ctx: &Context,
216        entity_type: &EntityType,
217        parent_id: Option<EntityId>,
218        name: &str,
219    ) -> Result<Entity> {
220        if !self.schemas.contains_key(&entity_type) {
221            return Err(EntityTypeNotFound(entity_type.clone()).into());
222        }
223
224        if let Some(parent) = &parent_id {
225            if !self.entity_exists(ctx, &parent) {
226                return Err(EntityNotFound(parent.clone()).into());
227            }
228        }
229
230        let entity_id = EntityId::new(entity_type.clone(), self.snowflake.generate());
231        if self.fields.contains_key(&entity_id) {
232            return Err(EntityExists(entity_id).into());
233        }
234
235        {
236            let entities = self
237                .entities
238                .entry(entity_type.clone())
239                .or_insert_with(Vec::new);
240            entities.push(entity_id.clone());
241        }
242
243        {
244            self.fields
245                .entry(entity_id.clone())
246                .or_insert_with(HashMap::new);
247        }
248
249        {
250            let mut writes = self
251                .schemas
252                .get(entity_type)
253                .map(|s| &s.fields)
254                .into_iter()
255                .flat_map(|fields| fields.iter())
256                .map(|(field_type, _)| match field_type.as_ref() {
257                    "Name" => {
258                        swrite!(entity_id.clone(), field_type.clone(), sstr!(name))
259                    }
260                    "Parent" => match &parent_id {
261                        Some(parent) => swrite!(
262                            entity_id.clone(),
263                            field_type.clone(),
264                            sref!(Some(parent.clone()))
265                        ),
266                        None => swrite!(entity_id.clone(), field_type.clone()),
267                    },
268                    _ => {
269                        // Write the field with its default value
270                        swrite!(entity_id.clone(), field_type.clone())
271                    }
272                })
273                .collect::<Vec<Request>>();
274
275            // If we have a parent, add it to the parent's children list
276            if let Some(parent) = &parent_id {
277                writes.push(sadd!(
278                    parent.clone(),
279                    "Children".into(),
280                    sreflist![entity_id.clone()]
281                ));
282            }
283
284            self.perform(ctx, &mut writes)?;
285        }
286
287        Ok(Entity::new(entity_id))
288    }
289
290    pub fn get_entity_schema(
291        &self,
292        _: &Context,
293        entity_type: &EntityType,
294    ) -> Result<EntitySchema<Single>> {
295        self.schemas
296            .get(entity_type)
297            .cloned()
298            .ok_or_else(|| EntityTypeNotFound(entity_type.clone()).into())
299    }
300
301    pub fn get_complete_entity_schema(
302        &self,
303        ctx: &Context,
304        entity_type: &EntityType,
305    ) -> Result<EntitySchema<Complete>> {
306        let mut schema = EntitySchema::<Complete>::from(self.get_entity_schema(ctx, entity_type)?);
307
308        loop {
309            if let Some(inherit_type) = &schema.inherit {
310                if let Some(inherit_schema) = self.schemas.get(inherit_type) {
311                    // Merge inherited fields into the current schema
312                    for (field_type, field_schema) in &inherit_schema.fields {
313                        schema
314                            .fields
315                            .entry(field_type.clone())
316                            .or_insert_with(|| field_schema.clone());
317                    }
318                } else {
319                    return Err(EntityTypeNotFound(inherit_type.clone()).into());
320                }
321            } else {
322                break;
323            }
324        }
325
326        Ok(schema)
327    }
328
329    /// Set or update the schema for an entity type
330    pub fn set_entity_schema(
331        &mut self,
332        ctx: &Context,
333        entity_schema: &EntitySchema<Single>,
334    ) -> Result<()> {
335        // Get a copy of the existing schema if it exists
336        // We'll use this to see if any fields have been added or removed
337        let complete_old_schema = self
338            .get_complete_entity_schema(ctx, &entity_schema.entity_type)
339            .unwrap_or_else(|_| EntitySchema::<Complete>::new(entity_schema.entity_type.clone()));
340
341        self.schemas
342            .insert(entity_schema.entity_type.clone(), entity_schema.clone());
343
344        if !self.entities.contains_key(&entity_schema.entity_type) {
345            self.entities
346                .insert(entity_schema.entity_type.clone(), Vec::new());
347        }
348
349        if !self.types.contains(&entity_schema.entity_type) {
350            self.types.push(entity_schema.entity_type.clone());
351        }
352
353        // Get the complete schema for the entity type
354        let complete_new_schema =
355            self.get_complete_entity_schema(ctx, &entity_schema.entity_type)?;
356
357        for removed_field in complete_old_schema.diff(&complete_new_schema) {
358            // If the field was removed, we need to remove it from all entities
359            for entity_id in self
360                .entities
361                .get(&entity_schema.entity_type)
362                .unwrap_or(&Vec::new())
363            {
364                if let Some(fields) = self.fields.get_mut(entity_id) {
365                    fields.remove(&removed_field.field_type);
366                }
367            }
368        }
369
370        for added_field in complete_new_schema.diff(&complete_old_schema) {
371            // If the field was added, we need to add it to all entities
372            for entity_id in self
373                .entities
374                .get(&entity_schema.entity_type)
375                .unwrap_or(&Vec::new())
376            {
377                let fields = self
378                    .fields
379                    .entry(entity_id.clone())
380                    .or_insert_with(HashMap::new);
381                fields.insert(
382                    added_field.field_type.clone(),
383                    Field {
384                        field_type: added_field.field_type.clone(),
385                        value: added_field.default_value.clone(),
386                        write_time: now(),
387                        writer_id: None,
388                    },
389                );
390            }
391        }
392
393        Ok(())
394    }
395
396    /// Get the schema for a specific field
397    pub fn get_field_schema(
398        &self,
399        ctx: &Context,
400        entity_type: &EntityType,
401        field_type: &FieldType,
402    ) -> Result<FieldSchema> {
403        self
404            .get_entity_schema(ctx, entity_type)?
405            .fields
406            .get(field_type)
407            .cloned()
408            .ok_or_else(|| {
409                FieldNotFound(EntityId::new(entity_type.clone(), 0), field_type.clone()).into()
410            })
411    }
412
413    /// Set or update the schema for a specific field
414    pub fn set_field_schema(
415        &mut self,
416        ctx: &Context,
417        entity_type: &EntityType,
418        field_type: &FieldType,
419        field_schema: FieldSchema,
420    ) -> Result<()> {
421        let mut entity_schema = self
422            .get_entity_schema(ctx, entity_type)?;
423
424        entity_schema.fields.insert(field_type.clone(), field_schema);
425
426        self.set_entity_schema(ctx, &entity_schema)
427    }
428
429    pub fn entity_exists(&self, _: &Context, entity_id: &EntityId) -> bool {
430        self.fields.contains_key(entity_id)
431    }
432
433    pub fn field_exists(
434        &self,
435        _: &Context,
436        entity_type: &EntityType,
437        field_type: &FieldType,
438    ) -> bool {
439        self.schemas
440            .get(entity_type)
441            .map(|schema| schema.fields.contains_key(field_type))
442            .unwrap_or(false)
443    }
444
445    pub fn perform(&mut self, ctx: &Context, requests: &mut Vec<Request>) -> Result<()> {
446        for request in requests.iter_mut() {
447            match request {
448                Request::Read {
449                    entity_id,
450                    field_type,
451                    value,
452                    write_time,
453                    writer_id,
454                } => {
455                    let indir: (EntityId, FieldType) =
456                        resolve_indirection(ctx, self, entity_id, field_type)?;
457                    self.read(ctx, &indir.0, &indir.1, value, write_time, writer_id)?;
458                }
459                Request::Write {
460                    entity_id,
461                    field_type,
462                    value,
463                    write_time,
464                    writer_id,
465                    push_condition,
466                    adjust_behavior,
467                } => {
468                    let indir = resolve_indirection(ctx, self, entity_id, field_type)?;
469                    self.write(
470                        ctx,
471                        &indir.0,
472                        &indir.1,
473                        value,
474                        write_time,
475                        writer_id,
476                        push_condition,
477                        adjust_behavior,
478                    )?;
479                }
480            }
481        }
482        Ok(())
483    }
484
485    fn read(
486        &self,
487        _: &Context,
488        entity_id: &EntityId,
489        field_type: &FieldType,
490        value: &mut Option<Value>,
491        write_time: &mut Option<Timestamp>,
492        writer_id: &mut Option<EntityId>,
493    ) -> Result<()> {
494        let field = self
495            .fields
496            .get(&entity_id)
497            .and_then(|fields| fields.get(field_type));
498
499        if let Some(field) = field {
500            *value = Some(field.value.clone());
501            *write_time = Some(field.write_time.clone());
502            *writer_id = field.writer_id.clone();
503        } else {
504            return Err(FieldNotFound(entity_id.clone(), field_type.clone()).into());
505        }
506
507        Ok(())
508    }
509
510    fn write(
511        &mut self,
512        ctx: &Context,
513        entity_id: &EntityId,
514        field_type: &FieldType,
515        value: &Option<Value>,
516        write_time: &Option<Timestamp>,
517        writer_id: &Option<EntityId>,
518        write_option: &PushCondition,
519        adjust_behavior: &AdjustBehavior,
520    ) -> Result<()> {
521        let entity_schema = self.get_complete_entity_schema(ctx, entity_id.get_type())?;
522        let field_schema = entity_schema.fields.get(field_type)
523            .ok_or_else(|| FieldNotFound(entity_id.clone(), field_type.clone()))?;
524
525        let fields = self
526            .fields
527            .entry(entity_id.clone())
528            .or_insert_with(HashMap::new);
529
530        let field = fields.entry(field_type.clone()).or_insert_with(|| Field {
531            field_type: field_type.clone(),
532            value: field_schema.default_value.clone(),
533            write_time: now(),
534            writer_id: None,
535        });
536
537        let mut new_value = field_schema.default_value.clone();
538        // Check that the value being written is the same type as the field schema
539        // If the value is None, use the default value from the schema
540        if let Some(value) = value {
541            if discriminant(value) != discriminant(&field_schema.default_value) {
542                return Err(ValueTypeMismatch(
543                    entity_id.clone(),
544                    field_type.clone(),
545                    field_schema.default_value.clone(),
546                    value.clone(),
547                )
548                .into());
549            }
550
551            new_value = value.clone();
552        }
553
554        let old_value = &field.value;
555        match adjust_behavior {
556            AdjustBehavior::Add => match old_value {
557                Value::Int(old_int) => {
558                    new_value = Value::Int(old_int + new_value.as_int().unwrap_or(0));
559                }
560                Value::Float(old_float) => {
561                    new_value = Value::Float(old_float + new_value.as_float().unwrap_or(0.0));
562                }
563                Value::EntityList(old_list) => {
564                    new_value = Value::EntityList(
565                        old_list
566                            .iter()
567                            .chain(new_value.as_entity_list().unwrap_or(&Vec::new()).iter())
568                            .cloned()
569                            .collect(),
570                    );
571                }
572                Value::String(old_string) => {
573                    new_value = Value::String(format!(
574                        "{}{}",
575                        old_string,
576                        new_value.as_string().cloned().unwrap_or_default()
577                    ));
578                }
579                Value::BinaryFile(old_file) => {
580                    new_value = Value::BinaryFile(
581                        old_file
582                            .iter()
583                            .chain(
584                                new_value
585                                    .as_binary_file()
586                                    .map_or(&Vec::new(), |f| &f)
587                                    .iter(),
588                            )
589                            .cloned()
590                            .collect(),
591                    );
592                }
593                _ => {
594                    return Err(UnsupportAdjustBehavior(
595                        entity_id.clone(),
596                        field_type.clone(),
597                        adjust_behavior.clone(),
598                    )
599                    .into());
600                }
601            },
602            AdjustBehavior::Subtract => match old_value {
603                Value::Int(old_int) => {
604                    new_value = Value::Int(old_int - new_value.as_int().unwrap_or(0));
605                }
606                Value::Float(old_float) => {
607                    new_value = Value::Float(old_float - new_value.as_float().unwrap_or(0.0));
608                }
609                Value::EntityList(old_list) => {
610                    let new_list = new_value.as_entity_list().cloned().unwrap_or_default();
611                    new_value = Value::EntityList(
612                        old_list
613                            .into_iter()
614                            .filter(|item| !new_list.contains(item))
615                            .cloned()
616                            .collect(),
617                    );
618                }
619                _ => {
620                    return Err(UnsupportAdjustBehavior(
621                        entity_id.clone(),
622                        field_type.clone(),
623                        adjust_behavior.clone(),
624                    )
625                    .into());
626                }
627            },
628            _ => {
629                // No adjustment needed
630            }
631        }
632
633        match write_option {
634            PushCondition::Always => {
635                field.value = new_value;
636
637                if let Some(write_time) = write_time {
638                    field.write_time = *write_time;
639                } else {
640                    field.write_time = now();
641                }
642                if let Some(writer_id) = writer_id {
643                    field.writer_id = Some(writer_id.clone());
644                } else {
645                    field.writer_id = None;
646                }
647            }
648            PushCondition::Changes => {
649                // Changes write, only update if the value is different
650                if field.value != new_value {
651                    field.value = new_value;
652                    if let Some(write_time) = write_time {
653                        field.write_time = *write_time;
654                    } else {
655                        field.write_time = now();
656                    }
657                    if let Some(writer_id) = writer_id {
658                        field.writer_id = Some(writer_id.clone());
659                    } else {
660                        field.writer_id = None;
661                    }
662                }
663            }
664        }
665
666        Ok(())
667    }
668
669    /// Deletes an entity and all its fields
670    /// Returns an error if the entity doesn't exist
671    pub fn delete_entity(&mut self, ctx: &Context, entity_id: &EntityId) -> Result<()> {
672        // Check if the entity exists
673        {
674            if !self.fields.contains_key(entity_id) {
675                return Err(EntityNotFound(entity_id.clone()).into());
676            }
677        }
678
679        // Remove all childrens
680        {
681            let mut reqs = vec![sread!(entity_id.clone(), "Children".into())];
682            self.perform(ctx, &mut reqs)?;
683            if let Request::Read { value, .. } = &reqs[0] {
684                if let Some(Value::EntityList(children)) = value {
685                    for child in children {
686                        self.delete_entity(ctx, child)?;
687                    }
688                } else {
689                    return Err(BadIndirection::new(
690                        entity_id.clone(),
691                        "Children".into(),
692                        BadIndirectionReason::UnexpectedValueType(
693                            "Children".into(),
694                            format!("{:?}", value),
695                        ),
696                    )
697                    .into());
698                }
699            }
700        }
701
702        // Remove from parent's children list
703        {
704            self.perform(
705                ctx,
706                &mut vec![ssub!(
707                    entity_id.clone(),
708                    "Parent->Children".into(),
709                    sreflist![entity_id.clone()]
710                )],
711            )?;
712        }
713
714        // Remove fields
715        {
716            self.fields.remove(entity_id);
717        }
718
719        // Remove from entity type list
720        {
721            if let Some(entities) = self.entities.get_mut(entity_id.get_type()) {
722                entities.retain(|id| id != entity_id);
723            }
724        }
725
726        Ok(())
727    }
728
729    /// Find entities of a specific type with pagination
730    pub fn find_entities(
731        &self,
732        _: &Context,
733        entity_type: &EntityType,
734        page_opts: Option<PageOpts>,
735    ) -> Result<PageResult<EntityId>> {
736        let opts = page_opts.unwrap_or_default();
737
738        // Check if entity type exists
739        if !self.entities.contains_key(entity_type) {
740            return Ok(PageResult {
741                items: Vec::new(),
742                total: 0,
743                next_cursor: None,
744            });
745        }
746
747        let all_entities = self.entities.get(entity_type).unwrap();
748        let total = all_entities.len();
749
750        // Find the starting index based on cursor
751        let start_idx = if let Some(cursor) = &opts.cursor {
752            match cursor.parse::<usize>() {
753                Ok(idx) => idx,
754                Err(_) => 0,
755            }
756        } else {
757            0
758        };
759
760        // Get the slice of entities for this page
761        let end_idx = std::cmp::min(start_idx + opts.limit, total);
762        let items: Vec<EntityId> = if start_idx < total {
763            all_entities[start_idx..end_idx].to_vec()
764        } else {
765            Vec::new()
766        };
767
768        // Calculate the next cursor
769        let next_cursor = if end_idx < total {
770            Some(end_idx.to_string())
771        } else {
772            None
773        };
774
775        Ok(PageResult {
776            items,
777            total,
778            next_cursor,
779        })
780    }
781
782    /// Get all entity types with pagination
783    pub fn get_entity_types(
784        &self,
785        _: &Context,
786        page_opts: Option<PageOpts>,
787    ) -> Result<PageResult<EntityType>> {
788        let opts = page_opts.unwrap_or_default();
789
790        // Collect all types from schema
791        let all_types: Vec<EntityType> = self.schemas.keys().cloned().collect();
792        let total = all_types.len();
793
794        // Find the starting index based on cursor
795        let start_idx = if let Some(cursor) = &opts.cursor {
796            match cursor.parse::<usize>() {
797                Ok(idx) => idx,
798                Err(_) => 0,
799            }
800        } else {
801            0
802        };
803
804        // Get the slice of types for this page
805        let end_idx = std::cmp::min(start_idx + opts.limit, total);
806        let items: Vec<EntityType> = if start_idx < total {
807            all_types[start_idx..end_idx].to_vec()
808        } else {
809            Vec::new()
810        };
811
812        // Calculate the next cursor
813        let next_cursor = if end_idx < total {
814            Some(end_idx.to_string())
815        } else {
816            None
817        };
818
819        Ok(PageResult {
820            items,
821            total,
822            next_cursor,
823        })
824    }
825
826    /// Take a snapshot of the current store state
827    pub fn take_snapshot(&self, _: &Context) -> Snapshot {
828        Snapshot {
829            schemas: self.schemas.clone(),
830            entities: self.entities.clone(),
831            types: self.types.clone(),
832            fields: self.fields.clone(),
833        }
834    }
835
836    /// Restore the store state from a snapshot
837    pub fn restore_snapshot(&mut self, _: &Context, snapshot: Snapshot) {
838        self.schemas = snapshot.schemas;
839        self.entities = snapshot.entities;
840        self.types = snapshot.types;
841        self.fields = snapshot.fields;
842    }
843}
844
845pub fn resolve_indirection(
846    ctx: &Context,
847    store: &mut Store,
848    entity_id: &EntityId,
849    field_type: &FieldType,
850) -> Result<(EntityId, FieldType)> {
851    let fields = field_type.indirect_fields();
852
853    if fields.len() == 1 {
854        return Ok((entity_id.clone(), field_type.clone()));
855    }
856
857    let mut current_entity_id = entity_id.clone();
858
859    for i in 0..fields.len() - 1 {
860        let field = &fields[i];
861
862        // Handle array index navigation (for EntityList fields)
863        if i > 0 && field.0.parse::<i64>().is_ok() {
864            let index = field.0.parse::<i64>().unwrap();
865            if index < 0 {
866                return Err(BadIndirection::new(
867                    current_entity_id.clone(),
868                    field_type.clone(),
869                    BadIndirectionReason::NegativeIndex(index),
870                )
871                .into());
872            }
873
874            // The previous field should have been an EntityList
875            let prev_field = &fields[i - 1];
876
877            let mut reqs = vec![sread!(current_entity_id.clone(), prev_field.clone())];
878            store.perform(ctx, &mut reqs)?;
879
880            if let Request::Read { value, .. } = &reqs[0] {
881                if let Some(Value::EntityList(entities)) = value {
882                    let index_usize = index as usize;
883                    if index_usize >= entities.len() {
884                        return Err(BadIndirection::new(
885                            current_entity_id.clone(),
886                            field_type.clone(),
887                            BadIndirectionReason::ArrayIndexOutOfBounds(
888                                index_usize,
889                                entities.len(),
890                            ),
891                        )
892                        .into());
893                    }
894
895                    current_entity_id = entities[index_usize].clone();
896                } else {
897                    return Err(BadIndirection::new(
898                        current_entity_id.clone(),
899                        field_type.clone(),
900                        BadIndirectionReason::UnexpectedValueType(
901                            prev_field.clone(),
902                            format!("{:?}", value),
903                        ),
904                    )
905                    .into());
906                }
907            }
908
909            continue;
910        }
911
912        // Normal field resolution
913        let mut reqs = vec![sread!(current_entity_id.clone(), field.clone())];
914
915        if let Err(e) = store.perform(ctx, &mut reqs) {
916            return Err(BadIndirection::new(
917                current_entity_id.clone(),
918                field_type.clone(),
919                BadIndirectionReason::FailedToResolveField(field.clone(), e.to_string()),
920            )
921            .into());
922        }
923
924        if let Request::Read { value, .. } = &reqs[0] {
925            if let Some(Value::EntityReference(reference)) = value {
926                match reference {
927                    Some(ref_id) => {
928                        // Check if the reference is valid
929                        if !store.entity_exists(ctx, ref_id) {
930                            return Err(BadIndirection::new(
931                                current_entity_id.clone(),
932                                field_type.clone(),
933                                BadIndirectionReason::InvalidEntityId(ref_id.clone()),
934                            )
935                            .into());
936                        }
937                        current_entity_id = ref_id.clone();
938                    }
939                    None => {
940                        // If the reference is None, this is an error
941                        return Err(BadIndirection::new(
942                            current_entity_id.clone(),
943                            field_type.clone(),
944                            BadIndirectionReason::EmptyEntityReference,
945                        )
946                        .into());
947                    }
948                }
949
950                continue;
951            }
952
953            if let Some(Value::EntityList(_)) = value {
954                // If next segment is not an index, this is an error
955                if i + 1 >= fields.len() - 1 || fields[i + 1].0.parse::<i64>().is_err() {
956                    return Err(BadIndirection::new(
957                        current_entity_id.clone(),
958                        field_type.clone(),
959                        BadIndirectionReason::ExpectedIndexAfterEntityList(fields[i + 1].clone()),
960                    )
961                    .into());
962                }
963                // The index will be processed in the next iteration
964                continue;
965            }
966
967            return Err(BadIndirection::new(
968                current_entity_id.clone(),
969                field_type.clone(),
970                BadIndirectionReason::UnexpectedValueType(field.clone(), format!("{:?}", value)),
971            )
972            .into());
973        }
974    }
975
976    Ok((current_entity_id, fields.last().unwrap().clone()))
977}