settingspec 0.1.2__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {settingspec-0.1.2 → settingspec-0.2.0}/Cargo.lock +1 -1
  2. {settingspec-0.1.2 → settingspec-0.2.0}/Cargo.toml +1 -1
  3. {settingspec-0.1.2 → settingspec-0.2.0}/PKG-INFO +1 -1
  4. {settingspec-0.1.2 → settingspec-0.2.0}/src/cli.rs +61 -5
  5. {settingspec-0.1.2 → settingspec-0.2.0}/LICENSE-APACHE +0 -0
  6. {settingspec-0.1.2 → settingspec-0.2.0}/LICENSE-MIT +0 -0
  7. {settingspec-0.1.2 → settingspec-0.2.0}/README.md +0 -0
  8. {settingspec-0.1.2 → settingspec-0.2.0}/pyproject.toml +0 -0
  9. {settingspec-0.1.2 → settingspec-0.2.0}/src/envfile.rs +0 -0
  10. {settingspec-0.1.2 → settingspec-0.2.0}/src/error.rs +0 -0
  11. {settingspec-0.1.2 → settingspec-0.2.0}/src/filter.rs +0 -0
  12. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/common.rs +0 -0
  13. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/env.rs +0 -0
  14. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/javascript.rs +0 -0
  15. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/json.rs +0 -0
  16. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/lua.rs +0 -0
  17. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/mod.rs +0 -0
  18. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/python.rs +0 -0
  19. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/toml.rs +0 -0
  20. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/tree.rs +0 -0
  21. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/typescript.rs +0 -0
  22. {settingspec-0.1.2 → settingspec-0.2.0}/src/generator/yaml.rs +0 -0
  23. {settingspec-0.1.2 → settingspec-0.2.0}/src/lib.rs +0 -0
  24. {settingspec-0.1.2 → settingspec-0.2.0}/src/main.rs +0 -0
  25. {settingspec-0.1.2 → settingspec-0.2.0}/src/model.rs +0 -0
  26. {settingspec-0.1.2 → settingspec-0.2.0}/src/parser.rs +0 -0
  27. {settingspec-0.1.2 → settingspec-0.2.0}/src/profile.rs +0 -0
  28. {settingspec-0.1.2 → settingspec-0.2.0}/src/resolver.rs +0 -0
@@ -564,7 +564,7 @@ dependencies = [
564
564
 
565
565
  [[package]]
566
566
  name = "settingspec"
567
- version = "0.1.2"
567
+ version = "0.2.0"
568
568
  dependencies = [
569
569
  "anyhow",
570
570
  "assert_cmd",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "settingspec"
3
- version = "0.1.2"
3
+ version = "0.2.0"
4
4
  edition = "2024"
5
5
  description = "One file for all environments, all languages, all submodules."
6
6
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: settingspec
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -23,6 +23,8 @@ pub struct Cli {
23
23
 
24
24
  #[derive(Subcommand, Debug)]
25
25
  pub enum Commands {
26
+ /// Initializes settingspec.toml in current directory
27
+ Init,
26
28
  /// Generates all configured export files and/or writes formatted output to stdout
27
29
  Export,
28
30
  /// Validates configuration syntax, profile completeness, and environment variables
@@ -34,19 +36,71 @@ pub enum Commands {
34
36
  },
35
37
  }
36
38
 
39
+ pub const INIT_CONFIG: &str = r#"[spec]
40
+ profile.options = ["local", "prod"]
41
+ profile.default = "local" # Switch via $SETTINGSPEC_PROFILE
42
+
43
+ [settings]
44
+ key.local.val = "local"
45
+ key.prod.env = "KEY" # Load from $KEY when active profile is "prod"
46
+ "#;
47
+
48
+ pub fn init_config() -> Result<()> {
49
+ let config_path = Path::new("settingspec.toml");
50
+ if config_path.exists() {
51
+ return Ok(());
52
+ }
53
+ std::fs::write(config_path, INIT_CONFIG)?;
54
+ Ok(())
55
+ }
56
+
37
57
  pub struct ExecutionContext {
38
58
  pub doc: SettingSpecDocument,
39
59
  pub active_profile: Option<String>,
40
60
  pub resolved_settings: Vec<ResolvedSetting>,
41
61
  pub sourced_env: HashMap<String, String>,
62
+ pub project_root: PathBuf,
63
+ }
64
+
65
+ pub fn find_project_root(start_dir: &Path) -> Option<PathBuf> {
66
+ let mut current = if start_dir.is_relative() {
67
+ match std::env::current_dir() {
68
+ Ok(cwd) => cwd.join(start_dir),
69
+ Err(_) => start_dir.to_path_buf(),
70
+ }
71
+ } else {
72
+ start_dir.to_path_buf()
73
+ };
74
+ if let Ok(canon) = current.canonicalize() {
75
+ current = canon;
76
+ } else {
77
+ current = normalize_path(&current);
78
+ }
79
+ loop {
80
+ let config = current.join("settingspec.toml");
81
+ if config.is_file() {
82
+ return Some(current);
83
+ }
84
+ if !current.pop() {
85
+ break;
86
+ }
87
+ }
88
+ None
42
89
  }
43
90
 
44
91
  pub fn load_and_resolve() -> Result<ExecutionContext> {
45
- let config_path = Path::new("settingspec.toml");
46
- if !config_path.exists() {
47
- return Err(SettingSpecError::ConfigNotFound);
92
+ let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
93
+ let project_root = find_project_root(&current_dir).ok_or(SettingSpecError::ConfigNotFound)?;
94
+
95
+ if let Ok(current_canon) = current_dir.canonicalize() {
96
+ if project_root != current_canon {
97
+ std::env::set_current_dir(&project_root)?;
98
+ }
99
+ } else if project_root != current_dir {
100
+ std::env::set_current_dir(&project_root)?;
48
101
  }
49
102
 
103
+ let config_path = Path::new("settingspec.toml");
50
104
  let content = std::fs::read_to_string(config_path)?;
51
105
  let doc = parse_config_str(&content)?;
52
106
 
@@ -64,6 +118,7 @@ pub fn load_and_resolve() -> Result<ExecutionContext> {
64
118
  active_profile,
65
119
  resolved_settings,
66
120
  sourced_env,
121
+ project_root,
67
122
  })
68
123
  }
69
124
 
@@ -71,6 +126,7 @@ pub fn run_cli() -> Result<()> {
71
126
  let cli = Cli::parse();
72
127
 
73
128
  match cli.command {
129
+ Commands::Init => init_config(),
74
130
  Commands::Check => {
75
131
  // Check only validates config syntax, profiles, env vars without creating files
76
132
  load_and_resolve()?;
@@ -312,8 +368,8 @@ pub fn auto_append_gitignore(ctx: &ExecutionContext) -> Result<()> {
312
368
  return Ok(());
313
369
  }
314
370
 
315
- let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
316
- let git_root = match find_git_root(&current_dir) {
371
+ let current_dir = &ctx.project_root;
372
+ let git_root = match find_git_root(current_dir) {
317
373
  Some(root) => root,
318
374
  None => return Ok(()),
319
375
  };
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes