Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
2 changes: 2 additions & 0 deletions lib/dsc-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ pub mod extensions;
pub mod functions;
pub mod parser;
pub mod progress;
pub mod settings;
pub mod types;
pub mod util;

// Re-export the dependency crate to minimize dependency management.
#[doc(inline)]
pub use dsc_lib_jsonschema as schemas;

i18n!("locales", fallback = "en-us");
Expand Down
119 changes: 119 additions & 0 deletions lib/dsc-lib/src/settings/constants_and_statics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Defines static lazy-initialized paths to the various settings files used by DSC. These paths
//! are determined at runtime based on the operating system and environment variables, and they
//! provide a consistent way to access the settings files across different platforms.

use std::{path::PathBuf, sync::{LazyLock}};

/// Name of the settings file used for the machine, user, and workspace scopes.
pub const SETTINGS_PREFERENCE_FILE_NAME: &str = "dsc.settings.json";
/// Name of the policy file used for the policy scope.
pub const SETTINGS_POLICY_FILE_NAME: &str = "dsc.policy.json";

/// Defines the full path to the policy settings file, which is located in a platform-specific
/// folder.
///
/// The pseudo-path for this file depends on the platform:
///
/// - On Windows: `{ProgramData}\dsc\dsc.policy.json`
/// - On macOS: `/Library/Application Support/dsc/dsc.policy.json`
/// - On Linux and other Unix-like systems: `/etc/dsc/dsc.policy.json`
pub static POLICY_SETTINGS_FILE_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
#[cfg(target_os = "windows")]
{
let program_data = std::env::var_os("ProgramData")
.expect("Couldn't retrieve the ProgramData environment variable");
std::path::Path::new(&program_data).join("dsc").join(SETTINGS_POLICY_FILE_NAME)
}
#[cfg(target_os = "macos")]
{
std::path::Path::new("/Library").join("Application Support").join("dsc").join(SETTINGS_POLICY_FILE_NAME)
}
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
{
std::path::Path::new("/etc").join("dsc").join(SETTINGS_POLICY_FILE_NAME)
}
});

/// Defines the full path to the machine settings file, which is located in a platform-specific
/// folder.
///
/// The pseudo-path for this file depends on the platform:
///
/// - On Windows: `{ProgramData}\dsc\dsc.settings.json`
/// - On macOS: `/Library/Application Support/dsc/dsc.settings.json`
/// - On Linux and other Unix-like systems: `/etc/dsc/dsc.settings.json`
pub static MACHINE_SETTINGS_FILE_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
#[cfg(target_os = "windows")]
{
let program_data = std::env::var_os("ProgramData")
.expect("Couldn't retrieve the ProgramData environment variable");
std::path::Path::new(&program_data).join("dsc").join(SETTINGS_PREFERENCE_FILE_NAME)
}
#[cfg(target_os = "macos")]
{
std::path::Path::new("/Library").join("Application Support").join("dsc").join(SETTINGS_PREFERENCE_FILE_NAME)
}
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
{
std::path::Path::new("/etc").join("dsc").join(SETTINGS_PREFERENCE_FILE_NAME)
}
});

/// Defines the full path to the user settings file, which is located in a platform-specific folder
/// based on the user's home directory or environment variables.
///
/// The pseudo-path for this file depends on the platform and whether the `XDG_CONFIG_HOME`
/// environment variable is set. When `XDG_CONFIG_HOME` is set, the path is always
/// `{XDG_CONFIG_HOME}/dsc/dsc.settings.json` (using `\` instead of `/` on Windows). Otherwise,
/// the path varies by platform:
///
/// - On Windows: `{APPDATA}\dsc\dsc.settings.json`
/// - On macOS: `{HOME}/Library/Application Support/dsc/dsc.settings.json`
/// - On Linux and other Unix-like systems: `{HOME}/.config/dsc/dsc.settings.json`
pub static USER_SETTINGS_FILE_PATH: LazyLock<std::path::PathBuf> = LazyLock::new(|| {
if let Some(xdg_config_home) = std::env::var_os("XDG_CONFIG_HOME") {
return std::path::Path::new(&xdg_config_home)
.join("dsc")
.join(SETTINGS_PREFERENCE_FILE_NAME);
}
#[cfg(target_os = "windows")]
{
let app_data = std::env::var_os("APPDATA")
.expect("Couldn't retrieve the APPDATA environment variable");
return std::path::Path::new(&app_data)
.join("dsc")
.join(SETTINGS_PREFERENCE_FILE_NAME);
}
#[cfg(target_os = "macos")]
{
let home = std::env::var_os("HOME")
.expect("Couldn't retrieve the HOME environment variable");
return std::path::Path::new(&home)
.join("Library")
.join("Application Support")
.join("dsc")
.join(SETTINGS_PREFERENCE_FILE_NAME);
}
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
{
let home = std::env::var_os("HOME")
.expect("Couldn't retrieve the HOME environment variable");
return std::path::Path::new(&home)
.join(".config")
.join("dsc")
.join(SETTINGS_PREFERENCE_FILE_NAME);
}
});

/// Defines the full path to the workspace settings file, which is located in the current working
/// directory.
///
/// The pseudo-path for this file is `{CWD}/dsc.settings.json`.
pub static WORKSPACE_SETTINGS_FILE_PATH: LazyLock<std::path::PathBuf> = LazyLock::new(|| {
std::env::current_dir()
.expect("Couldn't retrieve the current working directory")
.join(SETTINGS_PREFERENCE_FILE_NAME)
});
78 changes: 78 additions & 0 deletions lib/dsc-lib/src/settings/dsc_settings_scope.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use std::fmt::Display;

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// Defines the source of a setting value.
///
/// DSC supports multiple sources for settings. This enum represents the source of a setting value.
/// The sources are ordered by precedence, with the highest precedence source being the one that
/// DSC uses.
///
/// The highest precedence source is [`Policy`], which is defined in the machine policy file. Fields
/// defined as policy cannot be overridden by any other source, including environment variables or
/// command line arguments.
///
/// [`Policy`]: Self::Policy
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum DscSettingsScope {
/// The default settings staticalally defined in the DSC codebase.
Default,
/// The settings defined for all users on the machine in a [preference settings file].
///
/// The location for the settings file in this scope depends on the operating system:
///
/// - On Windows, it is typically located at `{PROGRAM_DATA}\DSC\machine_settings.json`.
/// - On Unix-like systems, it is typically located at `/etc/dsc/machine_settings.json`.
///
/// [preference settings file]: crate::settings::DscPreferenceFileData
Machine,
/// The settings defined for the current user in a [preference settings file].
///
/// [preference settings file]: crate::settings::DscPreferenceFileData
User,
/// The settings defined for the current workspace in a [preference settings file].
///
/// [preference settings file]: crate::settings::DscPreferenceFileData
Workspace,
/// Settings defined as environment variables.
Environment,
/// Settings defined as command line arguments.
#[serde(rename = "cli")]
CommandLine,
/// The system policy file. Fields defined as policy cannot be overridden.
Policy,
}

impl DscSettingsScope {
pub const ALL: [DscSettingsScope; 7] = [
DscSettingsScope::Default,
DscSettingsScope::Machine,
DscSettingsScope::User,
DscSettingsScope::Workspace,
DscSettingsScope::Environment,
DscSettingsScope::CommandLine,
DscSettingsScope::Policy,
];
pub const FILE_BASED: [DscSettingsScope; 3] = [
DscSettingsScope::Machine,
DscSettingsScope::User,
DscSettingsScope::Workspace,
];
}

impl Display for DscSettingsScope {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let source_str = match self {
DscSettingsScope::Default => "default",
DscSettingsScope::Machine => "machine",
DscSettingsScope::User => "user",
DscSettingsScope::Workspace => "workspace",
DscSettingsScope::Environment => "environment",
DscSettingsScope::CommandLine => "cli",
DscSettingsScope::Policy => "policy",
};
write!(f, "{}", source_str)
}
}
67 changes: 67 additions & 0 deletions lib/dsc-lib/src/settings/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use miette::Diagnostic;
use thiserror::Error;
use rust_i18n::t;

#[derive(Error, Debug, Diagnostic)]
pub enum DscSettingsError {
#[error("{t}", t = t!(
"settings.errors.invalidDataFileMultipleErrors",
path = file_path,
err = errors.iter().map(|e| e.to_string()).collect::<Vec<_>>().join(", ")
))]
InvalidDataFileMultipleErrors {
file_path: String,
#[related]
errors: Vec<DscSettingsError>,
},
#[error("{t}: {0}", t = t!("settings.errors.invalidIgnoreSettingsFileEnvVar"))]
InvalidIgnoreSettingsFileEnvVar(String),
#[error("{t}: {0}", t = t!("settings.errors.invalidTraceLevel"))]
InvalidTraceLevel(String),
#[error("{t}: {0}", t = t!("settings.errors.invalidTraceFormat"))]
InvalidTraceFormat(String),
/// The settings file could not be read.
#[error("{t}", t = t!("settings.errors.fileReadError", file_path = file_path))]
FileReadError {
file_path: String,
#[source]
source: std::io::Error,
},
/// The settings file could not be written.
#[error("{t}", t = t!("settings.errors.fileWriteError", file_path = file_path))]
FileWriteError{
file_path: String,
#[source]
source: std::io::Error,
},
/// The settings file could not be found.
#[error("{t}", t = t!("settings.errors.fileNotFound", path = file_path))]
FileNotFound{
file_path: String,
},
/// The settings file could not be parsed.
#[error("{t}", t = t!("settings.errors.parseDataFileError", path = file_path, err = source))]
ParseDataFileError{
file_path: String,
#[source]
source: serde_json::Error,
},
/// Indicates an error when parsing a boolean environment variable.
#[error("{t}", t = t!("settings.errors.parseBooleanEnvVarError", value = value))]
ParseBooleanEnvVarError{
value: String,
},
/// Multiple errors occurred while loading settings.
#[error("{t}: {0:?}", t = t!("settings.errors.loadMultipleErrors"))]
LoadMultipleErrors(Vec<DscSettingsError>),
/// Indicates an error when loading an environment variable.
#[error("{t}", t = t!("settings.errors.loadEnvironmentError"))]
LoadEnvironmentError{
env_var: &'static str,
#[source]
source: Box<DscSettingsError>,
},
/// Multiple errors occurred while loading settings from environment variables.
#[error("{t}: {0:?}", t = t!("settings.errors.loadEnvironmentMultipleErrors"))]
LoadEnvironmentMultipleErrors(Vec<DscSettingsError>),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// Defines the default value for the `forbid_ignore_settings_file` field in DSC settings.
pub const CODE_DEFAULT_FORBID_IGNORE_SETTINGS_FILE: bool = false;
1 change: 1 addition & 0 deletions lib/dsc-lib/src/settings/fields/ignore_settings_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const CODE_DEFAULT_IGNORE_SETTINGS_FILE: bool = false;
Loading
Loading