From b341bb0f4a23bac6cd929f1f835d7ea99baf7b50 Mon Sep 17 00:00:00 2001 From: Micah Date: Mon, 13 May 2024 12:07:56 -0700 Subject: [PATCH 1/6] Add YAML middleware --- Cargo.toml | 9 +- src/snapshot_middleware/mod.rs | 4 + ...leware__yaml__test__instance_from_vfs.snap | 20 +++ src/snapshot_middleware/yaml.rs | 116 ++++++++++++++++++ 4 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs.snap create mode 100644 src/snapshot_middleware/yaml.rs diff --git a/Cargo.toml b/Cargo.toml index a35ccd942..d2a4dec17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,11 @@ rbx_reflection = "4.5.0" rbx_reflection_database = "0.2.10" rbx_xml = "0.13.3" +serde = { version = "1.0.197", features = ["derive", "rc"] } +serde_json = "1.0.114" +serde_yaml = "0.8.26" +toml = "0.5.11" + anyhow = "1.0.80" backtrace = "0.3.69" bincode = "1.3.3" @@ -81,9 +86,6 @@ reqwest = { version = "0.11.24", default-features = false, features = [ ] } ritz = "0.1.0" roblox_install = "1.0.0" -serde = { version = "1.0.197", features = ["derive", "rc"] } -serde_json = "1.0.114" -toml = "0.5.11" termcolor = "1.4.1" thiserror = "1.0.57" tokio = { version = "1.36.0", features = ["rt", "rt-multi-thread"] } @@ -111,6 +113,5 @@ criterion = "0.3.6" insta = { version = "1.36.1", features = ["redactions", "yaml"] } paste = "1.0.14" pretty_assertions = "1.4.0" -serde_yaml = "0.8.26" tempfile = "3.10.1" walkdir = "2.5.0" diff --git a/src/snapshot_middleware/mod.rs b/src/snapshot_middleware/mod.rs index 1c95154ef..8a5a803bf 100644 --- a/src/snapshot_middleware/mod.rs +++ b/src/snapshot_middleware/mod.rs @@ -17,6 +17,7 @@ mod rbxmx; mod toml; mod txt; mod util; +mod yaml; use std::{ path::{Path, PathBuf}, @@ -41,6 +42,7 @@ use self::{ rbxmx::snapshot_rbxmx, toml::snapshot_toml, txt::snapshot_txt, + yaml::snapshot_yaml, }; pub use self::{project::snapshot_project_node, util::emit_legacy_scripts_default}; @@ -204,6 +206,7 @@ pub enum Middleware { Rbxmx, Toml, Text, + Yaml, Ignore, } @@ -229,6 +232,7 @@ impl Middleware { Self::Rbxmx => snapshot_rbxmx(context, vfs, path, name), Self::Toml => snapshot_toml(context, vfs, path, name), Self::Text => snapshot_txt(context, vfs, path, name), + Self::Yaml => snapshot_yaml(context, vfs, path, name), Self::Ignore => Ok(None), } } diff --git a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs.snap b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs.snap new file mode 100644 index 000000000..3e146d29d --- /dev/null +++ b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs.snap @@ -0,0 +1,20 @@ +--- +source: src/snapshot_middleware/yaml.rs +expression: instance_snapshot +--- +snapshot_id: "00000000000000000000000000000000" +metadata: + ignore_unknown_instances: false + instigating_source: + Path: /foo.yml + relevant_paths: + - /foo.yml + - /foo.meta.json + context: + emit_legacy_scripts: true +name: foo +class_name: ModuleScript +properties: + Source: + String: "return {\n\tstring = \"this is a string\",\n\tboolean = true,\n\tnumber = 1337,\n\t[\"value-with-hypen\"] = \"it sure is\",\n\tsequence = {\"wow\", 8675309},\n\tmap = {{\n\t\tkey = \"value\",\n\t}, {\n\t\tkey2 = \"value 2\",\n\t}, {\n\t\tkey3 = \"value 3\",\n\t}},\n\twhatever_this_is = {\"i imagine\", \"it's\", \"a\", \"sequence?\"},\n}" +children: [] diff --git a/src/snapshot_middleware/yaml.rs b/src/snapshot_middleware/yaml.rs new file mode 100644 index 000000000..d6ad8cceb --- /dev/null +++ b/src/snapshot_middleware/yaml.rs @@ -0,0 +1,116 @@ +use std::path::Path; + +use anyhow::Context; +use maplit::hashmap; +use memofs::{IoResultExt, Vfs}; + +use crate::{ + lua_ast::{Expression, Statement}, + snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot}, +}; + +use super::meta_file::AdjacentMetadata; + +pub fn snapshot_yaml( + context: &InstanceContext, + vfs: &Vfs, + path: &Path, + name: &str, +) -> anyhow::Result> { + let contents = vfs.read(path)?; + + let value: serde_yaml::Value = serde_yaml::from_slice(&contents) + .with_context(|| format!("File contains malformed YAML: {}", path.display()))?; + + let as_lua = yaml_to_lua(value).to_string(); + + let properties = hashmap! { + "Source".to_owned() => as_lua.into(), + }; + + let meta_path = path.with_file_name(format!("{}.meta.json", name)); + + let mut snapshot = InstanceSnapshot::new() + .name(name) + .class_name("ModuleScript") + .properties(properties) + .metadata( + InstanceMetadata::new() + .instigating_source(path) + .relevant_paths(vec![path.to_path_buf(), meta_path.clone()]) + .context(context), + ); + + if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? { + let mut metadata = AdjacentMetadata::from_slice(&meta_contents, meta_path)?; + metadata.apply_all(&mut snapshot)?; + } + + Ok(Some(snapshot)) +} + +fn yaml_to_lua(value: serde_yaml::Value) -> Statement { + Statement::Return(yaml_to_lua_value(value)) +} + +fn yaml_to_lua_value(value: serde_yaml::Value) -> Expression { + use serde_yaml::Value; + + match value { + Value::Bool(value) => Expression::Bool(value), + Value::Null => Expression::Nil, + Value::Number(value) => Expression::Number(value.as_f64().unwrap()), + Value::String(value) => Expression::String(value), + Value::Mapping(map) => Expression::table( + map.into_iter() + .map(|(key, value)| (yaml_to_lua_value(key), yaml_to_lua_value(value))) + .collect(), + ), + Value::Sequence(seq) => Expression::Array(seq.into_iter().map(yaml_to_lua_value).collect()), + } +} + +#[cfg(test)] +mod test { + use super::*; + + use memofs::{InMemoryFs, VfsSnapshot}; + + #[test] + fn instance_from_vfs() { + let mut imfs = InMemoryFs::new(); + imfs.load_snapshot( + "/foo.yml", + VfsSnapshot::file( + r#" +--- +string: this is a string +boolean: true +number: 1337 +value-with-hypen: it sure is +sequence: + - wow + - 8675309 +map: + - key: value + - key2: "value 2" + - key3: 'value 3' +whatever_this_is: [i imagine, it's, a, sequence?]"#, + ), + ) + .unwrap(); + + let vfs = Vfs::new(imfs.clone()); + + let instance_snapshot = snapshot_yaml( + &InstanceContext::default(), + &vfs, + Path::new("/foo.yml"), + "foo", + ) + .unwrap() + .unwrap(); + + insta::assert_yaml_snapshot!(instance_snapshot); + } +} From c28c044317621bf0f5e8ca981730ba1d008666e1 Mon Sep 17 00:00:00 2001 From: Micah Date: Mon, 13 May 2024 12:11:47 -0700 Subject: [PATCH 2/6] Update changelog (for now) --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05550ae26..f2cbece96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,8 @@ **All** sync rules are reset between project files, so they must be specified in each one when nesting them. This is to ensure that nothing can break other projects by changing how files are synced! +* Added the ability to sync `YAML` files into ModuleScripts in the same way that `TOML` and `JSON` files are synced. This is not enabled by default and must be enabled via an entry in `syncRules` with `use` set to `yaml`. ([#913]) + [#813]: https://github.com/rojo-rbx/rojo/pull/813 [#834]: https://github.com/rojo-rbx/rojo/pull/834 [#838]: https://github.com/rojo-rbx/rojo/pull/838 @@ -65,6 +67,7 @@ [#893]: https://github.com/rojo-rbx/rojo/pull/893 [#903]: https://github.com/rojo-rbx/rojo/pull/903 [#911]: https://github.com/rojo-rbx/rojo/pull/911 +[#913]: https://github.com/rojo-rbx/rojo/pull/913 ## [7.4.1] - February 20, 2024 * Made the `name` field optional on project files ([#870]) From 3c148a89f968946157cdfd3b4f7b9ad2f89920cd Mon Sep 17 00:00:00 2001 From: Micah Date: Thu, 23 May 2024 10:27:24 -0700 Subject: [PATCH 3/6] Address review feedback --- src/snapshot_middleware/yaml.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/snapshot_middleware/yaml.rs b/src/snapshot_middleware/yaml.rs index d6ad8cceb..3a5aa0c02 100644 --- a/src/snapshot_middleware/yaml.rs +++ b/src/snapshot_middleware/yaml.rs @@ -1,7 +1,6 @@ -use std::path::Path; +use std::{collections::HashMap, path::Path}; use anyhow::Context; -use maplit::hashmap; use memofs::{IoResultExt, Vfs}; use crate::{ @@ -24,11 +23,9 @@ pub fn snapshot_yaml( let as_lua = yaml_to_lua(value).to_string(); - let properties = hashmap! { - "Source".to_owned() => as_lua.into(), - }; + let properties = HashMap::from_iter([("Source".to_owned(), as_lua.into())]); - let meta_path = path.with_file_name(format!("{}.meta.json", name)); + let meta_path = path.with_file_name(format!("{name}.meta.json")); let mut snapshot = InstanceSnapshot::new() .name(name) From 0bf9521922e61588aa90c81d848aed8992874805 Mon Sep 17 00:00:00 2001 From: Micah Date: Thu, 23 May 2024 10:27:40 -0700 Subject: [PATCH 4/6] Add .yml and .yaml as file extensions for YAML middleware --- src/snapshot_middleware/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/snapshot_middleware/mod.rs b/src/snapshot_middleware/mod.rs index 8a5a803bf..d59ab2099 100644 --- a/src/snapshot_middleware/mod.rs +++ b/src/snapshot_middleware/mod.rs @@ -296,6 +296,7 @@ fn default_sync_rules() -> &'static [SyncRule] { sync_rule!("*.txt", Text), sync_rule!("*.rbxmx", Rbxmx), sync_rule!("*.rbxm", Rbxm), + sync_rule!("*.{yml,yaml}", Yaml), ] }) } From 6310c9147a52fa74561aa1dc73abbbbe07a9ea7a Mon Sep 17 00:00:00 2001 From: Micah Date: Thu, 23 May 2024 10:29:32 -0700 Subject: [PATCH 5/6] Update changelog to mention yml files --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2cbece96..0b0a20fc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,11 +52,12 @@ | `rbxm` | `.rbxm` | | `rbxmx` | `.rbxmx` | | `project` | `.project.json` | + | `yaml` | `.yml` | | `ignore` | None! | **All** sync rules are reset between project files, so they must be specified in each one when nesting them. This is to ensure that nothing can break other projects by changing how files are synced! -* Added the ability to sync `YAML` files into ModuleScripts in the same way that `TOML` and `JSON` files are synced. This is not enabled by default and must be enabled via an entry in `syncRules` with `use` set to `yaml`. ([#913]) +* Added the ability to sync `YAML` files into ModuleScripts in the same way that `TOML` and `JSON` files are synced. This will apply to `.yml` and `.yaml` files. ([#913]) [#813]: https://github.com/rojo-rbx/rojo/pull/813 [#834]: https://github.com/rojo-rbx/rojo/pull/834 From a350b6fca2684ad5902992d60d5c1597ae4c5f85 Mon Sep 17 00:00:00 2001 From: Micah Date: Thu, 30 May 2024 12:48:34 -0700 Subject: [PATCH 6/6] Correct indentation in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b68e6536..f63b87d7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,7 +53,7 @@ | `rbxm` | `.rbxm` | | `rbxmx` | `.rbxmx` | | `project` | `.project.json` | - | `yaml` | `.yml` | + | `yaml` | `.yml` | | `ignore` | None! | **All** sync rules are reset between project files, so they must be specified in each one when nesting them. This is to ensure that nothing can break other projects by changing how files are synced!