mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
lock down sparse settings exports in fabro-types
This commit is contained in:
parent
73a47c1256
commit
1bd7b7688f
17 changed files with 118 additions and 125 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1585,6 +1585,7 @@ name = "fabro-checkpoint"
|
|||
version = "0.212.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fabro-config",
|
||||
"fabro-store",
|
||||
"fabro-types",
|
||||
"git2",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ doctest = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
fabro-config = { path = "../fabro-config" }
|
||||
fabro-store = { path = "../fabro-store" }
|
||||
fabro-types = { path = "../fabro-types" }
|
||||
git2.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use fabro_config::GitAuthorLayer;
|
||||
use fabro_types::settings::InterpString;
|
||||
use fabro_types::settings::run::{GitAuthorLayer, GitAuthorSettings};
|
||||
use fabro_types::settings::run::GitAuthorSettings;
|
||||
|
||||
/// Resolved git author identity for checkpoint commits.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use fabro_types::settings::InterpString;
|
|||
pub use features::resolve_features;
|
||||
pub use project::resolve_project;
|
||||
pub use run::resolve_run;
|
||||
#[cfg(test)]
|
||||
pub(crate) use server::dev_token_auth_enabled;
|
||||
pub use server::resolve_server;
|
||||
pub use workflow::resolve_workflow;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ pub use run_id::{RunId, fixtures};
|
|||
pub use run_projection::{NodeState, PendingInterviewRecord, RunProjection};
|
||||
pub use run_summary::RunSummary;
|
||||
pub use sandbox_record::SandboxRecord;
|
||||
pub use settings::Combine;
|
||||
pub use stage_id::{ParallelBranchId, StageId};
|
||||
pub use start::StartRecord;
|
||||
pub use status::{
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ pub struct CliLoggingSettings {
|
|||
/// A sparse `[cli]` layer as it appears in a single settings file.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CliLayer {
|
||||
pub(crate) struct CliLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub target: Option<CliTargetLayer>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -92,7 +92,7 @@ pub struct CliLayer {
|
|||
/// `[cli.target]` — explicit transport selection.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields, tag = "type", rename_all = "lowercase")]
|
||||
pub enum CliTargetLayer {
|
||||
pub(crate) enum CliTargetLayer {
|
||||
Http {
|
||||
#[serde(default)]
|
||||
url: Option<InterpString>,
|
||||
|
|
@ -106,7 +106,7 @@ pub enum CliTargetLayer {
|
|||
/// `[cli.auth]` — explicit auth strategy selection.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CliAuthLayer {
|
||||
pub(crate) struct CliAuthLayer {
|
||||
/// `none` explicitly disables inherited auth.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub strategy: Option<CliAuthStrategy>,
|
||||
|
|
@ -122,7 +122,7 @@ pub enum CliAuthStrategy {
|
|||
/// `[cli.exec]` — `fabro exec` defaults.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CliExecLayer {
|
||||
pub(crate) struct CliExecLayer {
|
||||
/// Prevent idle sleep on macOS while an exec run is in flight.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub prevent_idle_sleep: Option<bool>,
|
||||
|
|
@ -134,7 +134,7 @@ pub struct CliExecLayer {
|
|||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CliExecModelLayer {
|
||||
pub(crate) struct CliExecModelLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider: Option<InterpString>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -143,7 +143,7 @@ pub struct CliExecModelLayer {
|
|||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CliExecAgentLayer {
|
||||
pub(crate) struct CliExecAgentLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub permissions: Option<AgentPermissions>,
|
||||
/// Agent-scoped MCP entries for `fabro exec`.
|
||||
|
|
@ -154,7 +154,7 @@ pub struct CliExecAgentLayer {
|
|||
/// `[cli.output]` — generic CLI output defaults.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CliOutputLayer {
|
||||
pub(crate) struct CliOutputLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub format: Option<OutputFormat>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -181,7 +181,7 @@ pub enum OutputVerbosity {
|
|||
/// `[cli.updates]` — upgrade check toggle.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CliUpdatesLayer {
|
||||
pub(crate) struct CliUpdatesLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub check: Option<bool>,
|
||||
}
|
||||
|
|
@ -189,7 +189,7 @@ pub struct CliUpdatesLayer {
|
|||
/// `[cli.logging]` — process-owned logging configuration for the CLI.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CliLoggingLayer {
|
||||
pub(crate) struct CliLoggingLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub level: Option<String>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use super::server::{
|
|||
};
|
||||
use super::size::Size;
|
||||
|
||||
pub trait Combine {
|
||||
pub(crate) trait Combine {
|
||||
/// Combine two values, preferring the values in `self`.
|
||||
#[must_use]
|
||||
fn combine(self, other: Self) -> Self;
|
||||
|
|
@ -137,7 +137,7 @@ impl Combine for RunCheckpointLayer {
|
|||
|
||||
/// An element of a splice-aware sequence: either a regular value or the
|
||||
/// `...` marker that asks the combiner to expand the fallback list inline.
|
||||
pub trait SpliceMarker {
|
||||
pub(crate) trait SpliceMarker {
|
||||
fn is_splice(&self) -> bool;
|
||||
}
|
||||
|
||||
|
|
@ -217,12 +217,21 @@ fn combine_hooks(fallback: &[HookEntry], current: Vec<HookEntry>) -> Vec<HookEnt
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, PartialEq, fabro_macros::Combine)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct FieldMergeLayer {
|
||||
a: Option<u32>,
|
||||
b: Option<u32>,
|
||||
}
|
||||
|
||||
impl Combine for FieldMergeLayer {
|
||||
fn combine(self, other: Self) -> Self {
|
||||
Self {
|
||||
a: self.a.combine(other.a),
|
||||
b: self.b.combine(other.b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct WholeReplaceLayer {
|
||||
a: Option<u32>,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ pub struct FeaturesNamespace {
|
|||
/// override a flag without forcing a default that hides an unset value.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct FeaturesLayer {
|
||||
pub(crate) struct FeaturesLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub session_sandboxes: Option<bool>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use super::workflow::WorkflowLayer;
|
|||
|
||||
/// A sparse settings layer before merge/resolve.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SettingsLayer {
|
||||
pub(crate) struct SettingsLayer {
|
||||
#[serde(default, rename = "_version", skip_serializing_if = "Option::is_none")]
|
||||
pub version: Option<u32>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -39,7 +39,7 @@ impl SettingsLayer {
|
|||
/// with `["dev-token"]`. Use anywhere a test needs a starter
|
||||
/// `SettingsLayer` that the strict resolver will accept.
|
||||
#[must_use]
|
||||
pub fn test_default() -> Self {
|
||||
pub(crate) fn test_default() -> Self {
|
||||
let mut layer = Self::default();
|
||||
layer.ensure_test_auth_methods();
|
||||
layer
|
||||
|
|
@ -48,7 +48,7 @@ impl SettingsLayer {
|
|||
/// If `server.auth.methods` is unset, populate it with `["dev-token"]`.
|
||||
/// Existing methods (set by a fixture) are preserved. Use to make a
|
||||
/// parsed-from-TOML layer resolve cleanly without overriding test intent.
|
||||
pub fn ensure_test_auth_methods(&mut self) {
|
||||
pub(crate) fn ensure_test_auth_methods(&mut self) {
|
||||
use super::server::{ServerAuthLayer, ServerAuthMethod, ServerLayer as ServerLayerTy};
|
||||
|
||||
if self
|
||||
|
|
|
|||
|
|
@ -8,26 +8,26 @@ use super::combine::Combine;
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ReplaceMap<V>(pub HashMap<String, V>);
|
||||
pub(crate) struct ReplaceMap<V>(pub HashMap<String, V>);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct StickyMap<V>(pub HashMap<String, V>);
|
||||
pub(crate) struct StickyMap<V>(pub HashMap<String, V>);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct MergeMap<V>(pub HashMap<String, V>);
|
||||
pub(crate) struct MergeMap<V>(pub HashMap<String, V>);
|
||||
|
||||
macro_rules! impl_map_wrapper {
|
||||
($name:ident) => {
|
||||
impl<V> $name<V> {
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn into_inner(self) -> HashMap<String, V> {
|
||||
pub(crate) fn into_inner(self) -> HashMap<String, V> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
|
@ -107,12 +107,21 @@ impl<V: Combine> Combine for MergeMap<V> {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, PartialEq, fabro_macros::Combine)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct ValueLayer {
|
||||
a: Option<String>,
|
||||
b: Option<String>,
|
||||
}
|
||||
|
||||
impl Combine for ValueLayer {
|
||||
fn combine(self, other: Self) -> Self {
|
||||
Self {
|
||||
a: self.a.combine(other.a),
|
||||
b: self.b.combine(other.b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replace_map_self_wins_when_non_empty() {
|
||||
let this = ReplaceMap(HashMap::from([("a".to_string(), "this".to_string())]));
|
||||
|
|
|
|||
|
|
@ -10,51 +10,46 @@
|
|||
//! exists.
|
||||
|
||||
pub mod cli;
|
||||
pub mod combine;
|
||||
mod combine;
|
||||
pub mod duration;
|
||||
pub mod features;
|
||||
pub mod interp;
|
||||
pub mod layer;
|
||||
pub mod maps;
|
||||
mod layer;
|
||||
mod maps;
|
||||
pub mod model_ref;
|
||||
pub mod project;
|
||||
pub mod run;
|
||||
pub mod server;
|
||||
pub mod size;
|
||||
pub mod splice_array;
|
||||
mod splice_array;
|
||||
pub mod workflow;
|
||||
|
||||
pub use cli::{
|
||||
CliAuthSettings, CliExecAgentSettings, CliExecModelSettings, CliExecSettings, CliLayer,
|
||||
CliAuthSettings, CliExecAgentSettings, CliExecModelSettings, CliExecSettings,
|
||||
CliLoggingSettings, CliNamespace, CliOutputSettings, CliTargetSettings, CliUpdatesSettings,
|
||||
};
|
||||
pub use combine::Combine;
|
||||
pub use duration::{Duration, ParseDurationError};
|
||||
pub use features::{FeaturesLayer, FeaturesNamespace};
|
||||
pub use features::FeaturesNamespace;
|
||||
pub use interp::{InterpString, Provenance, ResolveEnvError, Resolved};
|
||||
pub use layer::SettingsLayer;
|
||||
pub use maps::{MergeMap, ReplaceMap, StickyMap};
|
||||
pub use model_ref::{
|
||||
AmbiguousModelRef, ModelRef, ModelRegistry, ParseModelRefError, ResolvedModelRef,
|
||||
};
|
||||
pub use project::{ProjectLayer, ProjectNamespace};
|
||||
pub use project::ProjectNamespace;
|
||||
pub use run::{
|
||||
ArtifactsSettings, DaytonaSettings, DaytonaSnapshotSettings, DockerfileSource,
|
||||
GitAuthorSettings, HookDefinition, HookType, InterviewProviderSettings, McpServerSettings,
|
||||
McpTransport, NotificationProviderSettings, NotificationRouteSettings, PullRequestSettings,
|
||||
RunAgentSettings, RunCheckpointSettings, RunExecutionSettings, RunGitSettings, RunGoal,
|
||||
RunInterviewsSettings, RunLayer, RunModelSettings, RunNamespace, RunPrepareSettings,
|
||||
RunSandboxSettings, RunScmSettings, ScmGitHubSettings, TlsMode,
|
||||
RunInterviewsSettings, RunModelSettings, RunNamespace, RunPrepareSettings, RunSandboxSettings,
|
||||
RunScmSettings, ScmGitHubSettings, TlsMode,
|
||||
};
|
||||
pub use server::{
|
||||
DiscordIntegrationSettings, GithubIntegrationSettings, IntegrationWebhooksSettings,
|
||||
IpAllowEntry, ObjectStoreSettings, ServerApiSettings, ServerArtifactsSettings,
|
||||
ServerAuthGithubSettings, ServerAuthMethod, ServerAuthSettings, ServerIntegrationsSettings,
|
||||
ServerIpAllowlistLayer, ServerIpAllowlistOverrideLayer, ServerIpAllowlistOverrideSettings,
|
||||
ServerIpAllowlistSettings, ServerLayer, ServerListenSettings, ServerLoggingSettings,
|
||||
ServerNamespace, ServerSchedulerSettings, ServerSlateDbSettings, ServerStorageSettings,
|
||||
ServerWebSettings, SlackIntegrationSettings, TeamsIntegrationSettings,
|
||||
ServerIpAllowlistOverrideSettings, ServerIpAllowlistSettings, ServerListenSettings,
|
||||
ServerLoggingSettings, ServerNamespace, ServerSchedulerSettings, ServerSlateDbSettings,
|
||||
ServerStorageSettings, ServerWebSettings, SlackIntegrationSettings, TeamsIntegrationSettings,
|
||||
};
|
||||
pub use size::{ParseSizeError, Size};
|
||||
pub use splice_array::{SPLICE_MARKER, SpliceArray, SpliceArrayError};
|
||||
pub use workflow::{WorkflowLayer, WorkflowNamespace};
|
||||
pub use workflow::WorkflowNamespace;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ pub struct ProjectNamespace {
|
|||
/// A sparse `[project]` layer as it appears in a single settings file.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ProjectLayer {
|
||||
pub(crate) struct ProjectLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
|
|||
|
|
@ -429,7 +429,7 @@ pub struct ArtifactsSettings {
|
|||
/// A sparse `[run]` layer as it appears in a single settings file.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunLayer {
|
||||
pub(crate) struct RunLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub goal: Option<RunGoalLayer>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -491,7 +491,7 @@ pub struct RunLayer {
|
|||
/// effective working directory.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged, deny_unknown_fields)]
|
||||
pub enum RunGoalLayer {
|
||||
pub(crate) enum RunGoalLayer {
|
||||
Inline(InterpString),
|
||||
File { file: InterpString },
|
||||
}
|
||||
|
|
@ -520,7 +520,7 @@ pub enum ResolvedGoalSource {
|
|||
/// `[run.model]` — provider-neutral default model selection.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunModelLayer {
|
||||
pub(crate) struct RunModelLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider: Option<InterpString>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -533,7 +533,7 @@ pub struct RunModelLayer {
|
|||
|
||||
/// A single `fallbacks` entry: either a parsed `ModelRef` or the splice marker.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ModelRefOrSplice {
|
||||
pub(crate) enum ModelRefOrSplice {
|
||||
ModelRef(ModelRef),
|
||||
Splice,
|
||||
}
|
||||
|
|
@ -562,14 +562,14 @@ impl<'de> Deserialize<'de> for ModelRefOrSplice {
|
|||
/// `[run.git]` — local git behavior such as commit author.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunGitLayer {
|
||||
pub(crate) struct RunGitLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub author: Option<GitAuthorLayer>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct GitAuthorLayer {
|
||||
pub(crate) struct GitAuthorLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<InterpString>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -580,7 +580,7 @@ pub struct GitAuthorLayer {
|
|||
/// across layers.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunPrepareLayer {
|
||||
pub(crate) struct RunPrepareLayer {
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub steps: Vec<PrepareStep>,
|
||||
/// Optional timeout applied to each prepare step.
|
||||
|
|
@ -603,7 +603,7 @@ pub struct PrepareStep {
|
|||
/// `[run.execution]` — run posture knobs.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunExecutionLayer {
|
||||
pub(crate) struct RunExecutionLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub mode: Option<RunMode>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -630,7 +630,7 @@ pub enum ApprovalMode {
|
|||
/// `[run.checkpoint]` — checkpoint policy.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunCheckpointLayer {
|
||||
pub(crate) struct RunCheckpointLayer {
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub exclude_globs: Vec<String>,
|
||||
}
|
||||
|
|
@ -638,7 +638,7 @@ pub struct RunCheckpointLayer {
|
|||
/// `[run.sandbox]` — sandbox selection and execution-environment surface.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunSandboxLayer {
|
||||
pub(crate) struct RunSandboxLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -656,7 +656,7 @@ pub struct RunSandboxLayer {
|
|||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct LocalSandboxLayer {
|
||||
pub(crate) struct LocalSandboxLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub worktree_mode: Option<WorktreeMode>,
|
||||
}
|
||||
|
|
@ -673,7 +673,7 @@ pub enum WorktreeMode {
|
|||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct DaytonaSandboxLayer {
|
||||
pub(crate) struct DaytonaSandboxLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub auto_stop_interval: Option<i32>,
|
||||
/// Sticky merge-by-key (provider-native labels).
|
||||
|
|
@ -689,7 +689,7 @@ pub struct DaytonaSandboxLayer {
|
|||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct DaytonaSnapshotLayer {
|
||||
pub(crate) struct DaytonaSnapshotLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -704,7 +704,7 @@ pub struct DaytonaSnapshotLayer {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged, deny_unknown_fields)]
|
||||
pub enum DaytonaDockerfileLayer {
|
||||
pub(crate) enum DaytonaDockerfileLayer {
|
||||
Inline(String),
|
||||
Path { path: String },
|
||||
}
|
||||
|
|
@ -720,7 +720,7 @@ pub enum DaytonaNetworkLayer {
|
|||
/// `[run.notifications.<name>]` — a keyed notification route.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct NotificationRouteLayer {
|
||||
pub(crate) struct NotificationRouteLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -739,7 +739,7 @@ pub struct NotificationRouteLayer {
|
|||
|
||||
/// A single string array entry that may be the splice marker.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum StringOrSplice {
|
||||
pub(crate) enum StringOrSplice {
|
||||
Value(String),
|
||||
Splice,
|
||||
}
|
||||
|
|
@ -767,7 +767,7 @@ impl<'de> Deserialize<'de> for StringOrSplice {
|
|||
/// Provider-specific destination fields for a notification route.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct NotificationProviderLayer {
|
||||
pub(crate) struct NotificationProviderLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub channel: Option<InterpString>,
|
||||
}
|
||||
|
|
@ -775,7 +775,7 @@ pub struct NotificationProviderLayer {
|
|||
/// `[run.interviews]` — external interview delivery.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct InterviewsLayer {
|
||||
pub(crate) struct InterviewsLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -788,7 +788,7 @@ pub struct InterviewsLayer {
|
|||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct InterviewProviderLayer {
|
||||
pub(crate) struct InterviewProviderLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub channel: Option<InterpString>,
|
||||
}
|
||||
|
|
@ -796,7 +796,7 @@ pub struct InterviewProviderLayer {
|
|||
/// `[run.agent]` — agent knobs only (permissions, MCPs).
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunAgentLayer {
|
||||
pub(crate) struct RunAgentLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub permissions: Option<AgentPermissions>,
|
||||
/// Agent-scoped MCP server entries, keyed by name.
|
||||
|
|
@ -817,7 +817,7 @@ pub enum AgentPermissions {
|
|||
/// transports use neither field.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields, tag = "type", rename_all = "snake_case")]
|
||||
pub enum McpEntryLayer {
|
||||
pub(crate) enum McpEntryLayer {
|
||||
Http {
|
||||
#[serde(default)]
|
||||
enabled: Option<bool>,
|
||||
|
|
@ -865,7 +865,7 @@ pub enum McpEntryLayer {
|
|||
/// used for cross-layer replace-by-id merging.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct HookEntry {
|
||||
pub(crate) struct HookEntry {
|
||||
/// Optional merge identity. Hooks with the same `id` replace in place.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<String>,
|
||||
|
|
@ -906,7 +906,7 @@ pub struct HookEntry {
|
|||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum HookTlsMode {
|
||||
pub(crate) enum HookTlsMode {
|
||||
#[default]
|
||||
Verify,
|
||||
NoVerify,
|
||||
|
|
@ -918,7 +918,7 @@ pub enum HookTlsMode {
|
|||
/// struct without a discriminator.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum HookAgentMarker {
|
||||
pub(crate) enum HookAgentMarker {
|
||||
#[default]
|
||||
Enabled,
|
||||
}
|
||||
|
|
@ -947,7 +947,7 @@ pub enum HookEvent {
|
|||
/// `[run.scm]` — remote SCM host/provider behavior.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunScmLayer {
|
||||
pub(crate) struct RunScmLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -964,12 +964,12 @@ pub struct RunScmLayer {
|
|||
/// `run.pull_request` until a concrete use case lands.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ScmGitHubLayer;
|
||||
pub(crate) struct ScmGitHubLayer;
|
||||
|
||||
/// `[run.pull_request]` — provider-neutral PR behavior.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunPullRequestLayer {
|
||||
pub(crate) struct RunPullRequestLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -991,7 +991,7 @@ pub enum MergeStrategy {
|
|||
/// `[run.artifacts]` — run artifact collection policy.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RunArtifactsLayer {
|
||||
pub(crate) struct RunArtifactsLayer {
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub include: Vec<String>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ where
|
|||
/// A sparse `[server]` layer as it appears in a single settings file.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerLayer {
|
||||
pub(crate) struct ServerLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub listen: Option<ServerListenLayer>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -337,7 +337,7 @@ pub struct ServerLayer {
|
|||
/// `[server.listen]` — shared bind transport.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields, tag = "type", rename_all = "lowercase")]
|
||||
pub enum ServerListenLayer {
|
||||
pub(crate) enum ServerListenLayer {
|
||||
Tcp {
|
||||
#[serde(default)]
|
||||
address: Option<InterpString>,
|
||||
|
|
@ -353,7 +353,7 @@ pub enum ServerListenLayer {
|
|||
/// `url` is an optional public URL; it is **not** derived from `server.listen`.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerApiLayer {
|
||||
pub(crate) struct ServerApiLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub url: Option<InterpString>,
|
||||
}
|
||||
|
|
@ -361,7 +361,7 @@ pub struct ServerApiLayer {
|
|||
/// `[server.web]` — web surface settings.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerWebLayer {
|
||||
pub(crate) struct ServerWebLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -375,7 +375,7 @@ pub struct ServerWebLayer {
|
|||
/// explicitly opt in to insecure configurations.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerAuthLayer {
|
||||
pub(crate) struct ServerAuthLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub methods: Option<Vec<ServerAuthMethod>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -384,14 +384,14 @@ pub struct ServerAuthLayer {
|
|||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerAuthGithubLayer {
|
||||
pub(crate) struct ServerAuthGithubLayer {
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub allowed_usernames: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerIpAllowlistLayer {
|
||||
pub(crate) struct ServerIpAllowlistLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub entries: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -400,7 +400,7 @@ pub struct ServerIpAllowlistLayer {
|
|||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerIpAllowlistOverrideLayer {
|
||||
pub(crate) struct ServerIpAllowlistOverrideLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub entries: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -410,7 +410,7 @@ pub struct ServerIpAllowlistOverrideLayer {
|
|||
/// `[server.storage]` — single managed local disk root.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerStorageLayer {
|
||||
pub(crate) struct ServerStorageLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub root: Option<InterpString>,
|
||||
}
|
||||
|
|
@ -418,7 +418,7 @@ pub struct ServerStorageLayer {
|
|||
/// `[server.artifacts]` — object-store-backed artifact storage.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerArtifactsLayer {
|
||||
pub(crate) struct ServerArtifactsLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider: Option<ObjectStoreProvider>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -432,7 +432,7 @@ pub struct ServerArtifactsLayer {
|
|||
/// `[server.slatedb]` — SlateDB bottomless storage plus tunables.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerSlateDbLayer {
|
||||
pub(crate) struct ServerSlateDbLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider: Option<ObjectStoreProvider>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -458,7 +458,7 @@ pub enum ObjectStoreProvider {
|
|||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ObjectStoreLocalLayer {
|
||||
pub(crate) struct ObjectStoreLocalLayer {
|
||||
/// Overrides the default root, which otherwise falls back to
|
||||
/// `{server.storage.root}/objects/{domain}`.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -467,7 +467,7 @@ pub struct ObjectStoreLocalLayer {
|
|||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ObjectStoreS3Layer {
|
||||
pub(crate) struct ObjectStoreS3Layer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub bucket: Option<InterpString>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -481,7 +481,7 @@ pub struct ObjectStoreS3Layer {
|
|||
/// `[server.scheduler]` — server-managed execution policy.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerSchedulerLayer {
|
||||
pub(crate) struct ServerSchedulerLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_concurrent_runs: Option<usize>,
|
||||
}
|
||||
|
|
@ -489,7 +489,7 @@ pub struct ServerSchedulerLayer {
|
|||
/// `[server.logging]` — process-owned logging configuration for the server.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerLoggingLayer {
|
||||
pub(crate) struct ServerLoggingLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub level: Option<String>,
|
||||
}
|
||||
|
|
@ -500,7 +500,7 @@ pub struct ServerLoggingLayer {
|
|||
/// shape so strict unknown-field validation still holds.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ServerIntegrationsLayer {
|
||||
pub(crate) struct ServerIntegrationsLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub github: Option<GithubIntegrationLayer>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -515,7 +515,7 @@ pub struct ServerIntegrationsLayer {
|
|||
/// webhooks.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct GithubIntegrationLayer {
|
||||
pub(crate) struct GithubIntegrationLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -535,7 +535,7 @@ pub struct GithubIntegrationLayer {
|
|||
/// `[server.integrations.slack]` — Slack workspace credentials and defaults.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct SlackIntegrationLayer {
|
||||
pub(crate) struct SlackIntegrationLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -545,7 +545,7 @@ pub struct SlackIntegrationLayer {
|
|||
/// `[server.integrations.discord]` — Discord workspace configuration.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct DiscordIntegrationLayer {
|
||||
pub(crate) struct DiscordIntegrationLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled: Option<bool>,
|
||||
}
|
||||
|
|
@ -553,14 +553,14 @@ pub struct DiscordIntegrationLayer {
|
|||
/// `[server.integrations.teams]` — Microsoft Teams configuration.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct TeamsIntegrationLayer {
|
||||
pub(crate) struct TeamsIntegrationLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct IntegrationWebhooksLayer {
|
||||
pub(crate) struct IntegrationWebhooksLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub strategy: Option<WebhookStrategy>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ use serde::de::{self, Visitor};
|
|||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
/// The reserved literal that marks the splice insertion point.
|
||||
pub const SPLICE_MARKER: &str = "...";
|
||||
pub(crate) const SPLICE_MARKER: &str = "...";
|
||||
|
||||
/// A string array that may contain at most one splice marker.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct SpliceArray {
|
||||
pub(crate) struct SpliceArray {
|
||||
entries: Vec<Entry>,
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ enum Entry {
|
|||
|
||||
/// An error returned when a splice array fails validation.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum SpliceArrayError {
|
||||
pub(crate) enum SpliceArrayError {
|
||||
/// The array contained more than one splice marker.
|
||||
MultipleMarkers,
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ impl std::error::Error for SpliceArrayError {}
|
|||
|
||||
impl SpliceArray {
|
||||
/// Build a splice array from a raw `Vec<String>`.
|
||||
pub fn from_raw(raw: Vec<String>) -> Result<Self, SpliceArrayError> {
|
||||
pub(crate) fn from_raw(raw: Vec<String>) -> Result<Self, SpliceArrayError> {
|
||||
let mut entries = Vec::with_capacity(raw.len());
|
||||
let mut marker_count = 0;
|
||||
for item in raw {
|
||||
|
|
@ -68,7 +68,7 @@ impl SpliceArray {
|
|||
|
||||
/// Build a splice array with no inherited splice marker.
|
||||
#[must_use]
|
||||
pub fn from_values(values: impl IntoIterator<Item = String>) -> Self {
|
||||
pub(crate) fn from_values(values: impl IntoIterator<Item = String>) -> Self {
|
||||
Self {
|
||||
entries: values.into_iter().map(Entry::Value).collect(),
|
||||
}
|
||||
|
|
@ -76,19 +76,19 @@ impl SpliceArray {
|
|||
|
||||
/// True when the array contains a splice marker.
|
||||
#[must_use]
|
||||
pub fn has_splice(&self) -> bool {
|
||||
pub(crate) fn has_splice(&self) -> bool {
|
||||
self.entries.iter().any(|e| matches!(e, Entry::Splice))
|
||||
}
|
||||
|
||||
/// The index of the splice marker, if present.
|
||||
#[must_use]
|
||||
pub fn splice_position(&self) -> Option<usize> {
|
||||
pub(crate) fn splice_position(&self) -> Option<usize> {
|
||||
self.entries.iter().position(|e| matches!(e, Entry::Splice))
|
||||
}
|
||||
|
||||
/// The non-splice values, in source order.
|
||||
#[must_use]
|
||||
pub fn values(&self) -> Vec<&str> {
|
||||
pub(crate) fn values(&self) -> Vec<&str> {
|
||||
self.entries
|
||||
.iter()
|
||||
.filter_map(|e| match e {
|
||||
|
|
@ -105,7 +105,7 @@ impl SpliceArray {
|
|||
/// - If the array has no splice marker, it replaces the inherited list
|
||||
/// wholesale.
|
||||
#[must_use]
|
||||
pub fn resolve(self, inherited: Vec<String>) -> Vec<String> {
|
||||
pub(crate) fn resolve(self, inherited: Vec<String>) -> Vec<String> {
|
||||
let Some(pos) = self.splice_position() else {
|
||||
return self
|
||||
.entries
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ pub struct WorkflowNamespace {
|
|||
/// A sparse `[workflow]` layer as it appears in a single settings file.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct WorkflowLayer {
|
||||
pub(crate) struct WorkflowLayer {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
use fabro_types::settings::SettingsLayer;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn settings_layer_round_trips_github_integration_strategy() {
|
||||
let source = json!({
|
||||
"_version": 1,
|
||||
"server": {
|
||||
"integrations": {
|
||||
"github": {
|
||||
"strategy": "token",
|
||||
"app_id": "{{ env.GITHUB_APP_ID }}"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let settings: SettingsLayer =
|
||||
serde_json::from_value(source.clone()).expect("settings should deserialize");
|
||||
let round_trip = serde_json::to_value(&settings).expect("settings should serialize");
|
||||
|
||||
assert_eq!(round_trip, source);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue