use std::{borrow::Cow, str}; use maplit::hashmap; use rbx_dom_weak::{RbxId, RbxTree, RbxValue}; use crate::{ imfs::{FileSnapshot, FsResultExt, Imfs, ImfsEntry, ImfsFetcher, ImfsSnapshot}, snapshot::{InstanceMetadata, InstanceSnapshot}, }; use super::{ context::InstanceSnapshotContext, error::SnapshotError, meta_file::AdjacentMetadata, middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware}, util::match_file_name, }; pub struct SnapshotTxt; impl SnapshotMiddleware for SnapshotTxt { fn from_imfs( _context: &InstanceSnapshotContext, imfs: &mut Imfs, entry: &ImfsEntry, ) -> SnapshotInstanceResult<'static> { if entry.is_directory() { return Ok(None); } let instance_name = match match_file_name(entry.path(), ".txt") { Some(name) => name, None => return Ok(None), }; let contents = entry.contents(imfs)?; let contents_str = str::from_utf8(contents) .map_err(|err| SnapshotError::file_contents_bad_unicode(err, entry.path()))? .to_string(); let properties = hashmap! { "Value".to_owned() => RbxValue::String { value: contents_str, }, }; let meta_path = entry .path() .with_file_name(format!("{}.meta.json", instance_name)); let mut snapshot = InstanceSnapshot { snapshot_id: None, metadata: InstanceMetadata { instigating_source: Some(entry.path().to_path_buf().into()), relevant_paths: vec![entry.path().to_path_buf(), meta_path.clone()], ..Default::default() }, name: Cow::Owned(instance_name.to_owned()), class_name: Cow::Borrowed("StringValue"), properties, children: Vec::new(), }; if let Some(meta_entry) = imfs.get(meta_path).with_not_found()? { let meta_contents = meta_entry.contents(imfs)?; let mut metadata = AdjacentMetadata::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 != "StringValue" { return None; } if !instance.get_children_ids().is_empty() { return None; } let value = match instance.properties.get("Value") { Some(RbxValue::String { value }) => value.clone(), Some(_) => panic!("wrong type ahh"), None => String::new(), }; let snapshot = ImfsSnapshot::File(FileSnapshot { contents: value.into_bytes(), }); let mut file_name = instance.name.clone(); file_name.push_str(".txt"); Some((file_name, snapshot)) } } #[cfg(test)] mod test { use super::*; use insta::assert_yaml_snapshot; use maplit::hashmap; use rbx_dom_weak::RbxInstanceProperties; use crate::imfs::{ImfsDebug, NoopFetcher}; #[test] fn instance_from_imfs() { let mut imfs = Imfs::new(NoopFetcher); let file = ImfsSnapshot::file("Hello there!"); imfs.debug_load_snapshot("/foo.txt", file); let entry = imfs.get("/foo.txt").unwrap(); let instance_snapshot = SnapshotTxt::from_imfs(&InstanceSnapshotContext::default(), &mut imfs, &entry) .unwrap() .unwrap(); assert_yaml_snapshot!(instance_snapshot); } #[test] fn imfs_from_instance() { let tree = RbxTree::new(string_value("Root", "Hello, world!")); let root_id = tree.get_root_id(); let (_file_name, _file) = SnapshotTxt::from_instance(&tree, root_id).unwrap(); } fn folder(name: impl Into) -> RbxInstanceProperties { RbxInstanceProperties { name: name.into(), class_name: "Folder".to_owned(), properties: Default::default(), } } fn string_value(name: impl Into, value: impl Into) -> RbxInstanceProperties { RbxInstanceProperties { name: name.into(), class_name: "StringValue".to_owned(), properties: hashmap! { "Value".to_owned() => RbxValue::String { value: value.into(), }, }, } } }