use std::{path::Path, str}; use anyhow::Context as _; use memofs::Vfs; use rbx_dom_weak::types::Variant; use rbx_dom_weak::ustr; use crate::{ snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot}, syncback::{FsSnapshot, SyncbackReturn, SyncbackSnapshot}, }; use super::{meta_file::AdjacentMetadata, PathExt as _}; pub fn snapshot_txt( context: &InstanceContext, vfs: &Vfs, path: &Path, name: &str, ) -> anyhow::Result> { let contents = vfs.read_to_string(path)?; let contents_str = contents.as_str(); let mut snapshot = InstanceSnapshot::new() .name(name) .class_name("StringValue") .property(ustr("Value"), contents_str) .metadata( InstanceMetadata::new() .instigating_source(path) .relevant_paths(vec![vfs.canonicalize(path)?]) .context(context), ); AdjacentMetadata::read_and_apply_all(vfs, path, name, &mut snapshot)?; Ok(Some(snapshot)) } pub fn syncback_txt<'sync>( snapshot: &SyncbackSnapshot<'sync>, ) -> anyhow::Result> { let new_inst = snapshot.new_inst(); let contents = if let Some(Variant::String(source)) = new_inst.properties.get(&ustr("Value")) { source.as_bytes().to_vec() } else { anyhow::bail!("StringValues must have a `Value` property that is a String"); }; let mut fs_snapshot = FsSnapshot::new(); fs_snapshot.add_file(&snapshot.path, contents); let meta = AdjacentMetadata::from_syncback_snapshot(snapshot, snapshot.path.clone())?; if let Some(mut meta) = meta { // StringValues have relatively few properties that we care about, so // shifting is fine. meta.properties.shift_remove(&ustr("Value")); if !meta.is_empty() { let parent = snapshot.path.parent_err()?; let file_name = snapshot .path .file_name() .and_then(|n| n.to_str()) .unwrap_or(""); let meta_stem = file_name.strip_suffix(".txt").unwrap_or(file_name); fs_snapshot.add_file( parent.join(format!("{meta_stem}.meta.json")), serde_json::to_vec_pretty(&meta).context("could not serialize metadata")?, ); } } Ok(SyncbackReturn { fs_snapshot, children: Vec::new(), removed_children: Vec::new(), }) } #[cfg(test)] mod test { use super::*; use memofs::{InMemoryFs, VfsSnapshot}; #[test] fn instance_from_vfs() { let mut imfs = InMemoryFs::new(); imfs.load_snapshot("/foo.txt", VfsSnapshot::file("Hello there!")) .unwrap(); let vfs = Vfs::new(imfs.clone()); let instance_snapshot = snapshot_txt( &InstanceContext::default(), &vfs, Path::new("/foo.txt"), "foo", ) .unwrap() .unwrap(); insta::assert_yaml_snapshot!(instance_snapshot); } #[test] fn with_metadata() { let mut imfs = InMemoryFs::new(); imfs.load_snapshot("/foo.txt", VfsSnapshot::file("Hello there!")) .unwrap(); imfs.load_snapshot( "/foo.meta.json", VfsSnapshot::file( r#"{ "id": "manually specified" }"#, ), ) .unwrap(); let vfs = Vfs::new(imfs.clone()); let instance_snapshot = snapshot_txt( &InstanceContext::default(), &vfs, Path::new("/foo.txt"), "foo", ) .unwrap() .unwrap(); insta::assert_yaml_snapshot!(instance_snapshot); } }