From 47ac412b78f52ee937748f94c630260849f4c533 Mon Sep 17 00:00:00 2001 From: Ole Magnus Fon Johnsen Date: Mon, 9 Mar 2026 11:53:46 +0100 Subject: [PATCH] feat: add comments in header with metadata --- Cargo.lock | 97 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/schema/mod.rs | 19 +++++++++ src/schema/tests.rs | 22 +++++----- 4 files changed, 128 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af5fc9c..2f94612 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.21" @@ -67,6 +76,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "aws-lc-rs" version = "1.16.1" @@ -129,6 +144,7 @@ dependencies = [ name = "cenv" version = "0.1.0" dependencies = [ + "chrono", "clap", "dotenvy", "regex", @@ -156,6 +172,19 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + [[package]] name = "clap" version = "4.5.60" @@ -521,6 +550,30 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -750,6 +803,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -1656,6 +1718,41 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 842ee1c..b29a74c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +chrono = "0.4.44" clap = { version = "4", features = ["derive"] } dotenvy = "0.15" regex = "1" diff --git a/src/schema/mod.rs b/src/schema/mod.rs index e96131b..a2952a3 100644 --- a/src/schema/mod.rs +++ b/src/schema/mod.rs @@ -106,6 +106,25 @@ impl TryFrom for EntryKind { pub fn generate_env(schema: &Schema, existing_env: &HashMap) -> String { let mut output = String::new(); + // Add header comments and metadata + let version = option_env!("CENV_VERSION").unwrap_or("unknown"); + output.push_str("# This file is auto-generated by cenv. Do not edit directly.\n"); + output.push_str("# To update this file, edit the schema and run `cenv fix`.\n"); + output.push_str(format!("#schema-version={}\n", version).as_str()); + output.push_str(format!("#generated-at={}\n\n", chrono::Utc::now().to_rfc3339()).as_str()); + + // Add the key=value pairs for each entry in the schema, + // using existing env values or defaults as needed. + let env_content = generate_env_vars(schema, existing_env); + output.push_str(&env_content); + + output +} + +// Generates the key=value lines for the .env file based on the schema and existing env values. +fn generate_env_vars(schema: &Schema, existing_env: &HashMap) -> String { + let mut output = String::new(); + for entry in &schema.entries { // Determine the value to use: // 1. Use existing value from .env if present and non-empty diff --git a/src/schema/tests.rs b/src/schema/tests.rs index 849d61f..ac63ecd 100644 --- a/src/schema/tests.rs +++ b/src/schema/tests.rs @@ -31,7 +31,7 @@ fn default_options() -> Entry { default_entry() } -// ==================== generate_env Tests ==================== +// ==================== generate_env_vars Tests ==================== mod generate_env_tests { use super::*; @@ -40,7 +40,7 @@ mod generate_env_tests { fn empty_schema_produces_empty_output() { let schema = create_schema(vec![]); let existing = HashMap::new(); - let output = generate_env(&schema, &existing); + let output = generate_env_vars(&schema, &existing); assert_eq!(output, ""); } @@ -48,7 +48,7 @@ mod generate_env_tests { fn single_entry_no_existing_no_default() { let schema = create_schema(vec![create_entry("FOO", default_options())]); let existing = HashMap::new(); - let output = generate_env(&schema, &existing); + let output = generate_env_vars(&schema, &existing); assert_eq!(output, "FOO=\n"); } @@ -58,7 +58,7 @@ mod generate_env_tests { opts.default = Some("bar".to_string()); let schema = create_schema(vec![create_entry("FOO", opts)]); let existing = HashMap::new(); - let output = generate_env(&schema, &existing); + let output = generate_env_vars(&schema, &existing); assert_eq!(output, "FOO=bar\n"); } @@ -69,7 +69,7 @@ mod generate_env_tests { let schema = create_schema(vec![create_entry("FOO", opts)]); let mut existing = HashMap::new(); existing.insert("FOO".to_string(), "existing_value".to_string()); - let output = generate_env(&schema, &existing); + let output = generate_env_vars(&schema, &existing); assert_eq!(output, "FOO=existing_value\n"); } @@ -80,7 +80,7 @@ mod generate_env_tests { let schema = create_schema(vec![create_entry("FOO", opts)]); let mut existing = HashMap::new(); existing.insert("FOO".to_string(), "".to_string()); - let output = generate_env(&schema, &existing); + let output = generate_env_vars(&schema, &existing); assert_eq!(output, "FOO=default_value\n"); } @@ -89,7 +89,7 @@ mod generate_env_tests { let schema = create_schema(vec![create_entry("FOO", default_options())]); let mut existing = HashMap::new(); existing.insert("FOO".to_string(), "".to_string()); - let output = generate_env(&schema, &existing); + let output = generate_env_vars(&schema, &existing); assert_eq!(output, "FOO=\n"); } @@ -101,7 +101,7 @@ mod generate_env_tests { create_entry("CCC", default_options()), ]); let existing = HashMap::new(); - let output = generate_env(&schema, &existing); + let output = generate_env_vars(&schema, &existing); assert_eq!(output, "AAA=\nBBB=\nCCC=\n"); } @@ -118,7 +118,7 @@ mod generate_env_tests { let mut existing = HashMap::new(); existing.insert("AAA".to_string(), "value_a".to_string()); existing.insert("CCC".to_string(), "value_c".to_string()); - let output = generate_env(&schema, &existing); + let output = generate_env_vars(&schema, &existing); assert_eq!(output, "AAA=value_a\nBBB=default_b\nCCC=value_c\n"); } @@ -129,7 +129,7 @@ mod generate_env_tests { existing.insert("FOO".to_string(), "foo_value".to_string()); existing.insert("BAR".to_string(), "bar_value".to_string()); existing.insert("BAZ".to_string(), "baz_value".to_string()); - let output = generate_env(&schema, &existing); + let output = generate_env_vars(&schema, &existing); assert_eq!(output, "FOO=foo_value\n"); } @@ -141,7 +141,7 @@ mod generate_env_tests { "FOO".to_string(), "value with spaces & special=chars!".to_string(), ); - let output = generate_env(&schema, &existing); + let output = generate_env_vars(&schema, &existing); assert_eq!(output, "FOO=value with spaces & special=chars!\n"); } }