use std::{borrow::Cow, collections::HashMap}; use rbx_dom_weak::{RbxId, RbxTree}; use crate::{ snapshot::{InstanceMetadata, InstanceSnapshot}, vfs::{DirectorySnapshot, FsResultExt, Vfs, VfsEntry, VfsFetcher, VfsSnapshot}, }; use super::{ context::InstanceSnapshotContext, error::SnapshotError, meta_file::DirectoryMetadata, middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware}, snapshot_from_instance, snapshot_from_vfs, }; pub struct SnapshotDir; impl SnapshotMiddleware for SnapshotDir { fn from_vfs( context: &mut InstanceSnapshotContext, vfs: &Vfs, entry: &VfsEntry, ) -> SnapshotInstanceResult { if entry.is_file() { return Ok(None); } let children: Vec = entry.children(vfs)?; let mut snapshot_children = Vec::new(); for child in children.into_iter() { if let Some(child_snapshot) = snapshot_from_vfs(context, vfs, &child)? { snapshot_children.push(child_snapshot); } } let instance_name = entry .path() .file_name() .expect("Could not extract file name") .to_str() .ok_or_else(|| SnapshotError::file_name_bad_unicode(entry.path()))? .to_string(); let meta_path = entry.path().join("init.meta.json"); let relevant_paths = vec![ entry.path().to_path_buf(), meta_path.clone(), // TODO: We shouldn't need to know about Lua existing in this // middleware. Should we figure out a way for that function to add // relevant paths to this middleware? entry.path().join("init.lua"), entry.path().join("init.server.lua"), entry.path().join("init.client.lua"), ]; let mut snapshot = InstanceSnapshot { snapshot_id: None, metadata: InstanceMetadata { instigating_source: Some(entry.path().to_path_buf().into()), relevant_paths, ..Default::default() }, name: Cow::Owned(instance_name), class_name: Cow::Borrowed("Folder"), properties: HashMap::new(), children: snapshot_children, }; if let Some(meta_entry) = vfs.get(meta_path).with_not_found()? { let meta_contents = meta_entry.contents(vfs)?; let mut metadata = DirectoryMetadata::from_slice(&meta_contents); metadata.apply_all(&mut snapshot); } Ok(Some(snapshot)) } fn from_instance(tree: &RbxTree, id: RbxId) -> SnapshotFileResult { let instance = tree.get_instance(id).unwrap(); if instance.class_name != "Folder" { return None; } let mut children = HashMap::new(); for child_id in instance.get_children_ids() { if let Some((name, child)) = snapshot_from_instance(tree, *child_id) { children.insert(name, child); } } let snapshot = VfsSnapshot::Directory(DirectorySnapshot { children }); Some((instance.name.clone(), snapshot)) } } #[cfg(test)] mod test { use super::*; use insta::assert_yaml_snapshot; use maplit::hashmap; use crate::vfs::{NoopFetcher, VfsDebug}; #[test] fn empty_folder() { let mut vfs = Vfs::new(NoopFetcher); let dir = VfsSnapshot::dir::(HashMap::new()); vfs.debug_load_snapshot("/foo", dir); let entry = vfs.get("/foo").unwrap(); let instance_snapshot = SnapshotDir::from_vfs(&mut InstanceSnapshotContext::default(), &mut vfs, &entry) .unwrap() .unwrap(); assert_yaml_snapshot!(instance_snapshot); } #[test] fn folder_in_folder() { let mut vfs = Vfs::new(NoopFetcher); let dir = VfsSnapshot::dir(hashmap! { "Child" => VfsSnapshot::dir::(HashMap::new()), }); vfs.debug_load_snapshot("/foo", dir); let entry = vfs.get("/foo").unwrap(); let instance_snapshot = SnapshotDir::from_vfs(&mut InstanceSnapshotContext::default(), &mut vfs, &entry) .unwrap() .unwrap(); assert_yaml_snapshot!(instance_snapshot); } }