···1414pub use resource::*;
15151616mod system;
1717+use ::rusqlite::params;
1718pub use system::*;
18191920use std::path::Path;
···113114 ) -> Result<impl Iterator<Item = Entity<'a>> + 'a, Error> {
114115 let query = query::Query::<(), _>::new(self, components);
115116 query.try_into_iter()
117117+ }
118118+}
119119+120120+impl Ecs {
121121+ /// Returns the highest `last_modified` from all components of `entity`.
122122+ /// Returns `chrono::DateTime::MIN_UTC` if `entity` has no components
123123+ fn try_last_modified(
124124+ &self,
125125+ entity: EntityId,
126126+ ) -> Result<chrono::DateTime<chrono::Utc>, rusqlite::Error> {
127127+ let mut stmt = self.conn.prepare_cached(
128128+ "select max(last_modified) as last_modified from components where entity = ?",
129129+ )?;
130130+131131+ let last_modified = stmt
132132+ .query_map(params![&entity], |row| {
133133+ row.get::<_, String>("last_modified")
134134+ })?
135135+ .map(|dt| dt.expect("Valid chrono::DateTime"))
136136+ .next();
137137+138138+ let last_modified = if let Some(last_modified) = last_modified {
139139+ chrono::DateTime::parse_from_rfc3339(&last_modified)
140140+ .expect("Valid chrono::DateTime")
141141+ .to_utc()
142142+ } else {
143143+ chrono::DateTime::<chrono::Utc>::MIN_UTC
144144+ };
145145+146146+ Ok(last_modified)
116147 }
117148}
118149···465496 assert!(e2.matches::<A>());
466497 assert!(e2.matches::<B>());
467498 assert!(e2.matches::<(A, B)>());
499499+ }
500500+501501+ #[test]
502502+ fn last_modified() {
503503+ #[derive(Serialize, Deserialize, Component)]
504504+ struct A;
505505+ #[derive(Serialize, Deserialize, Component)]
506506+ struct B;
507507+508508+ let db = super::Ecs::open_in_memory().unwrap();
509509+ let e = db.new_entity().attach(A);
510510+511511+ assert!(e.last_modified() > chrono::Utc::now() - chrono::Duration::minutes(1));
512512+513513+ let old = e.last_modified();
514514+515515+ std::thread::sleep(std::time::Duration::from_millis(2));
516516+517517+ e.attach(B);
518518+ assert!(e.last_modified() > old);
468519 }
469520}
+65
src/migrations/01_last_modified.sql
···11+begin;
22+33+alter table components
44+rename to components_old;
55+66+create table components (
77+ entity integer not null,
88+ component text not null,
99+ data blob,
1010+ last_modified rfc3339 not null default (strftime ('%Y-%m-%dT%H:%M:%fZ'))
1111+);
1212+1313+create unique index if not exists components_entity_component_unqiue_idx on components (entity, component);
1414+1515+create index if not exists components_component_idx on components (component);
1616+1717+insert into
1818+ components (entity, component, data)
1919+select
2020+ *
2121+from
2222+ components_old;
2323+2424+create trigger if not exists components_last_modified_trigger before
2525+update on components for each row begin
2626+update components
2727+set
2828+ last_modified = strftime ('%Y-%m-%dT%H:%M:%fZ')
2929+where
3030+ entity = new.entity
3131+ and component = new.component;
3232+3333+end;
3434+3535+alter table resources
3636+rename to resources_old;
3737+3838+create table resources (
3939+ name text not null unique,
4040+ data blob,
4141+ last_modified rfc3339 not null default (strftime ('%Y-%m-%dT%H:%M:%fZ'))
4242+);
4343+4444+insert into
4545+ resources (name, data)
4646+select
4747+ *
4848+from
4949+ resources_old;
5050+5151+create trigger if not exists resources_last_modified_trigger before
5252+update on resources for each row begin
5353+update resources
5454+set
5555+ last_modified = strftime ('%Y-%m-%dT%H:%M:%fZ')
5656+where
5757+ name = new.name;
5858+5959+end;
6060+6161+drop table components_old;
6262+6363+drop table resources_old;
6464+6565+commit;
+28-2
src/schema.sql
···11create table if not exists components (
22 entity integer not null,
33 component text not null,
44- data blob
44+ data blob,
55+ last_modified rfc3339 not null default (strftime ('%Y-%m-%dT%H:%M:%fZ'))
56);
6778create unique index if not exists components_entity_component_unqiue_idx on components (entity, component);
89910create index if not exists components_component_idx on components (component);
10111111-create table if not exists resources (name text not null unique, data blob);
1212+create trigger if not exists components_last_modified_trigger before
1313+update on components for each row begin
1414+update components
1515+set
1616+ last_modified = strftime ('%Y-%m-%dT%H:%M:%fZ')
1717+where
1818+ entity = new.entity
1919+ and component = new.component;
2020+2121+end;
2222+2323+create table if not exists resources (
2424+ name text not null unique,
2525+ data blob,
2626+ last_modified rfc3339 not null default (strftime ('%Y-%m-%dT%H:%M:%fZ'))
2727+);
2828+2929+create trigger if not exists resources_last_modified_trigger before
3030+update on resources for each row begin
3131+update resources
3232+set
3333+ last_modified = strftime ('%Y-%m-%dT%H:%M:%fZ')
3434+where
3535+ name = new.name;
3636+3737+end;