@@ -1,15 +1,17 @@
use std::sync::Arc;
+use anyhow::{Result, bail};
use collections::IndexMap;
use convert_case::{Case, Casing as _};
use fs::Fs;
use gpui::{App, SharedString};
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use settings::{Settings as _, update_settings_file};
+use settings::{
+ AgentProfileContent, ContextServerPresetContent, Settings as _, SettingsContent,
+ update_settings_file,
+};
use util::ResultExt as _;
-use crate::AgentSettings;
+use crate::{AgentProfileId, AgentSettings};
pub mod builtin_profiles {
use super::AgentProfileId;
@@ -23,27 +25,6 @@ pub mod builtin_profiles {
}
}
-#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
-pub struct AgentProfileId(pub Arc<str>);
-
-impl AgentProfileId {
- pub fn as_str(&self) -> &str {
- &self.0
- }
-}
-
-impl std::fmt::Display for AgentProfileId {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
-impl Default for AgentProfileId {
- fn default() -> Self {
- Self("write".into())
- }
-}
-
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AgentProfile {
id: AgentProfileId,
@@ -87,10 +68,10 @@ impl AgentProfile {
.unwrap_or_default(),
};
- update_settings_file::<AgentSettings>(fs, cx, {
+ update_settings_file(fs, cx, {
let id = id.clone();
move |settings, _cx| {
- settings.create_profile(id, profile_settings).log_err();
+ profile_settings.save_to_settings(id, settings).log_err();
}
});
@@ -129,9 +110,71 @@ impl AgentProfileSettings {
.get(server_id)
.is_some_and(|preset| preset.tools.get(tool_name) == Some(&true))
}
+
+ pub fn save_to_settings(
+ &self,
+ profile_id: AgentProfileId,
+ content: &mut SettingsContent,
+ ) -> Result<()> {
+ let profiles = content
+ .agent
+ .get_or_insert_default()
+ .profiles
+ .get_or_insert_default();
+ if profiles.contains_key(&profile_id.0) {
+ bail!("profile with ID '{profile_id}' already exists");
+ }
+
+ profiles.insert(
+ profile_id.0.clone(),
+ AgentProfileContent {
+ name: self.name.clone().into(),
+ tools: self.tools.clone(),
+ enable_all_context_servers: Some(self.enable_all_context_servers),
+ context_servers: self
+ .context_servers
+ .clone()
+ .into_iter()
+ .map(|(server_id, preset)| {
+ (
+ server_id,
+ ContextServerPresetContent {
+ tools: preset.tools,
+ },
+ )
+ })
+ .collect(),
+ },
+ );
+
+ Ok(())
+ }
+}
+
+impl From<AgentProfileContent> for AgentProfileSettings {
+ fn from(content: AgentProfileContent) -> Self {
+ Self {
+ name: content.name.into(),
+ tools: content.tools,
+ enable_all_context_servers: content.enable_all_context_servers.unwrap_or_default(),
+ context_servers: content
+ .context_servers
+ .into_iter()
+ .map(|(server_id, preset)| (server_id, preset.into()))
+ .collect(),
+ }
+ }
}
#[derive(Debug, Clone, Default)]
pub struct ContextServerPreset {
pub tools: IndexMap<Arc<str>, bool>,
}
+
+impl From<settings::ContextServerPresetContent> for ContextServerPreset {
+ fn from(content: settings::ContextServerPresetContent) -> Self {
+ Self {
+ tools: content.tools,
+ }
+ }
+}
@@ -2,14 +2,16 @@ mod agent_profile;
use std::sync::Arc;
-use anyhow::{Result, bail};
use collections::IndexMap;
-use gpui::{App, Pixels, SharedString};
+use gpui::{App, Pixels, px};
use language_model::LanguageModel;
-use schemars::{JsonSchema, json_schema};
+use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
-use std::borrow::Cow;
+use settings::{
+ AgentDockPosition, DefaultAgentView, LanguageModelParameters, LanguageModelSelection,
+ NotifyWhenAgentWaiting, Settings, SettingsContent,
+};
+use util::MergeFrom;
pub use crate::agent_profile::*;
@@ -22,32 +24,6 @@ pub fn init(cx: &mut App) {
AgentSettings::register(cx);
}
-#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum AgentDockPosition {
- Left,
- #[default]
- Right,
- Bottom,
-}
-
-#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum DefaultView {
- #[default]
- Thread,
- TextThread,
-}
-
-#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
-#[serde(rename_all = "snake_case")]
-pub enum NotifyWhenAgentWaiting {
- #[default]
- PrimaryScreen,
- AllScreens,
- Never,
-}
-
#[derive(Default, Clone, Debug)]
pub struct AgentSettings {
pub enabled: bool,
@@ -60,9 +36,8 @@ pub struct AgentSettings {
pub commit_message_model: Option<LanguageModelSelection>,
pub thread_summary_model: Option<LanguageModelSelection>,
pub inline_alternatives: Vec<LanguageModelSelection>,
- pub using_outdated_settings_version: bool,
pub default_profile: AgentProfileId,
- pub default_view: DefaultView,
+ pub default_view: DefaultAgentView,
pub profiles: IndexMap<AgentProfileId, AgentProfileSettings>,
pub always_allow_tool_actions: bool,
pub notify_when_agent_waiting: NotifyWhenAgentWaiting,
@@ -80,71 +55,20 @@ pub struct AgentSettings {
impl AgentSettings {
pub fn temperature_for_model(model: &Arc<dyn LanguageModel>, cx: &App) -> Option<f32> {
let settings = Self::get_global(cx);
- settings
- .model_parameters
- .iter()
- .rfind(|setting| setting.matches(model))
- .and_then(|m| m.temperature)
- }
-
- pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
- self.inline_assistant_model = Some(LanguageModelSelection {
- provider: provider.into(),
- model,
- });
- }
-
- pub fn set_commit_message_model(&mut self, provider: String, model: String) {
- self.commit_message_model = Some(LanguageModelSelection {
- provider: provider.into(),
- model,
- });
- }
-
- pub fn set_thread_summary_model(&mut self, provider: String, model: String) {
- self.thread_summary_model = Some(LanguageModelSelection {
- provider: provider.into(),
- model,
- });
- }
-}
-
-#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
-pub struct LanguageModelParameters {
- pub provider: Option<LanguageModelProviderSetting>,
- pub model: Option<SharedString>,
- pub temperature: Option<f32>,
-}
-
-impl LanguageModelParameters {
- pub fn matches(&self, model: &Arc<dyn LanguageModel>) -> bool {
- if let Some(provider) = &self.provider
- && provider.0 != model.provider_id().0
- {
- return false;
- }
- if let Some(setting_model) = &self.model
- && *setting_model != model.id().0
- {
- return false;
+ for setting in settings.model_parameters.iter().rev() {
+ if let Some(provider) = &setting.provider
+ && provider.0 != model.provider_id().0
+ {
+ continue;
+ }
+ if let Some(setting_model) = &setting.model
+ && *setting_model != model.id().0
+ {
+ continue;
+ }
+ return setting.temperature;
}
- true
- }
-}
-
-impl AgentSettingsContent {
- pub fn set_dock(&mut self, dock: AgentDockPosition) {
- self.dock = Some(dock);
- }
-
- pub fn set_model(&mut self, language_model: Arc<dyn LanguageModel>) {
- let model = language_model.id().0.to_string();
- let provider = language_model.provider_id().0.to_string();
-
- self.default_model = Some(LanguageModelSelection {
- provider: provider.into(),
- model,
- });
+ return None;
}
pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
@@ -167,159 +91,6 @@ impl AgentSettingsContent {
model,
});
}
-
- pub fn set_always_allow_tool_actions(&mut self, allow: bool) {
- self.always_allow_tool_actions = Some(allow);
- }
-
- pub fn set_play_sound_when_agent_done(&mut self, allow: bool) {
- self.play_sound_when_agent_done = Some(allow);
- }
-
- pub fn set_single_file_review(&mut self, allow: bool) {
- self.single_file_review = Some(allow);
- }
-
- pub fn set_use_modifier_to_send(&mut self, always_use: bool) {
- self.use_modifier_to_send = Some(always_use);
- }
-
- pub fn set_profile(&mut self, profile_id: AgentProfileId) {
- self.default_profile = Some(profile_id);
- }
-
- pub fn create_profile(
- &mut self,
- profile_id: AgentProfileId,
- profile_settings: AgentProfileSettings,
- ) -> Result<()> {
- let profiles = self.profiles.get_or_insert_default();
- if profiles.contains_key(&profile_id) {
- bail!("profile with ID '{profile_id}' already exists");
- }
-
- profiles.insert(
- profile_id,
- AgentProfileContent {
- name: profile_settings.name.into(),
- tools: profile_settings.tools,
- enable_all_context_servers: Some(profile_settings.enable_all_context_servers),
- context_servers: profile_settings
- .context_servers
- .into_iter()
- .map(|(server_id, preset)| {
- (
- server_id,
- ContextServerPresetContent {
- tools: preset.tools,
- },
- )
- })
- .collect(),
- },
- );
-
- Ok(())
- }
-}
-
-#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, Default, SettingsUi, SettingsKey)]
-#[settings_key(key = "agent", fallback_key = "assistant")]
-pub struct AgentSettingsContent {
- /// Whether the Agent is enabled.
- ///
- /// Default: true
- enabled: Option<bool>,
- /// Whether to show the agent panel button in the status bar.
- ///
- /// Default: true
- button: Option<bool>,
- /// Where to dock the agent panel.
- ///
- /// Default: right
- dock: Option<AgentDockPosition>,
- /// Default width in pixels when the agent panel is docked to the left or right.
- ///
- /// Default: 640
- default_width: Option<f32>,
- /// Default height in pixels when the agent panel is docked to the bottom.
- ///
- /// Default: 320
- default_height: Option<f32>,
- /// The default model to use when creating new chats and for other features when a specific model is not specified.
- default_model: Option<LanguageModelSelection>,
- /// Model to use for the inline assistant. Defaults to default_model when not specified.
- inline_assistant_model: Option<LanguageModelSelection>,
- /// Model to use for generating git commit messages. Defaults to default_model when not specified.
- commit_message_model: Option<LanguageModelSelection>,
- /// Model to use for generating thread summaries. Defaults to default_model when not specified.
- thread_summary_model: Option<LanguageModelSelection>,
- /// Additional models with which to generate alternatives when performing inline assists.
- inline_alternatives: Option<Vec<LanguageModelSelection>>,
- /// The default profile to use in the Agent.
- ///
- /// Default: write
- default_profile: Option<AgentProfileId>,
- /// Which view type to show by default in the agent panel.
- ///
- /// Default: "thread"
- default_view: Option<DefaultView>,
- /// The available agent profiles.
- pub profiles: Option<IndexMap<AgentProfileId, AgentProfileContent>>,
- /// Whenever a tool action would normally wait for your confirmation
- /// that you allow it, always choose to allow it.
- ///
- /// This setting has no effect on external agents that support permission modes, such as Claude Code.
- ///
- /// Set `agent_servers.claude.default_mode` to `bypassPermissions`, to disable all permission requests when using Claude Code.
- ///
- /// Default: false
- always_allow_tool_actions: Option<bool>,
- /// Where to show a popup notification when the agent is waiting for user input.
- ///
- /// Default: "primary_screen"
- notify_when_agent_waiting: Option<NotifyWhenAgentWaiting>,
- /// Whether to play a sound when the agent has either completed its response, or needs user input.
- ///
- /// Default: false
- play_sound_when_agent_done: Option<bool>,
- /// Whether to stream edits from the agent as they are received.
- ///
- /// Default: false
- stream_edits: Option<bool>,
- /// Whether to display agent edits in single-file editors in addition to the review multibuffer pane.
- ///
- /// Default: true
- single_file_review: Option<bool>,
- /// Additional parameters for language model requests. When making a request
- /// to a model, parameters will be taken from the last entry in this list
- /// that matches the model's provider and name. In each entry, both provider
- /// and model are optional, so that you can specify parameters for either
- /// one.
- ///
- /// Default: []
- #[serde(default)]
- model_parameters: Vec<LanguageModelParameters>,
- /// What completion mode to enable for new threads
- ///
- /// Default: normal
- preferred_completion_mode: Option<CompletionMode>,
- /// Whether to show thumb buttons for feedback in the agent panel.
- ///
- /// Default: true
- enable_feedback: Option<bool>,
- /// Whether to have edit cards in the agent panel expanded, showing a preview of the full diff.
- ///
- /// Default: true
- expand_edit_card: Option<bool>,
- /// Whether to have terminal cards in the agent panel expanded, showing the whole command output.
- ///
- /// Default: true
- expand_terminal_card: Option<bool>,
- /// Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages in the agent panel.
- ///
- /// Default: false
- use_modifier_to_send: Option<bool>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)]
@@ -340,202 +111,136 @@ impl From<CompletionMode> for cloud_llm_client::CompletionMode {
}
}
-#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
-pub struct LanguageModelSelection {
- pub provider: LanguageModelProviderSetting,
- pub model: String,
+impl From<settings::CompletionMode> for CompletionMode {
+ fn from(value: settings::CompletionMode) -> Self {
+ match value {
+ settings::CompletionMode::Normal => CompletionMode::Normal,
+ settings::CompletionMode::Burn => CompletionMode::Burn,
+ }
+ }
}
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct LanguageModelProviderSetting(pub String);
-
-impl JsonSchema for LanguageModelProviderSetting {
- fn schema_name() -> Cow<'static, str> {
- "LanguageModelProviderSetting".into()
- }
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
+pub struct AgentProfileId(pub Arc<str>);
- fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
- json_schema!({
- "enum": [
- "amazon-bedrock",
- "anthropic",
- "copilot_chat",
- "deepseek",
- "google",
- "lmstudio",
- "mistral",
- "ollama",
- "openai",
- "openrouter",
- "vercel",
- "x_ai",
- "zed.dev"
- ]
- })
+impl AgentProfileId {
+ pub fn as_str(&self) -> &str {
+ &self.0
}
}
-impl From<String> for LanguageModelProviderSetting {
- fn from(provider: String) -> Self {
- Self(provider)
+impl std::fmt::Display for AgentProfileId {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)
}
}
-impl From<&str> for LanguageModelProviderSetting {
- fn from(provider: &str) -> Self {
- Self(provider.to_string())
+impl Default for AgentProfileId {
+ fn default() -> Self {
+ Self("write".into())
}
}
-#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema)]
-pub struct AgentProfileContent {
- pub name: Arc<str>,
- #[serde(default)]
- pub tools: IndexMap<Arc<str>, bool>,
- /// Whether all context servers are enabled by default.
- pub enable_all_context_servers: Option<bool>,
- #[serde(default)]
- pub context_servers: IndexMap<Arc<str>, ContextServerPresetContent>,
-}
-
-#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema)]
-pub struct ContextServerPresetContent {
- pub tools: IndexMap<Arc<str>, bool>,
-}
-
impl Settings for AgentSettings {
- const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]);
-
- type FileContent = AgentSettingsContent;
-
- fn load(
- sources: SettingsSources<Self::FileContent>,
- _: &mut gpui::App,
- ) -> anyhow::Result<Self> {
- let mut settings = AgentSettings::default();
+ // todo!() test preserved keys logic
+ fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
+ let agent = content.agent.clone().unwrap();
+ Self {
+ enabled: agent.enabled.unwrap(),
+ button: agent.button.unwrap(),
+ dock: agent.dock.unwrap(),
+ default_width: px(agent.default_width.unwrap()),
+ default_height: px(agent.default_height.unwrap()),
+ default_model: Some(agent.default_model.unwrap()),
+ inline_assistant_model: agent.inline_assistant_model,
+ commit_message_model: agent.commit_message_model,
+ thread_summary_model: agent.thread_summary_model,
+ inline_alternatives: agent.inline_alternatives.unwrap_or_default(),
+ default_profile: AgentProfileId(agent.default_profile.unwrap()),
+ default_view: agent.default_view.unwrap(),
+ profiles: agent
+ .profiles
+ .unwrap()
+ .into_iter()
+ .map(|(key, val)| (AgentProfileId(key), val.into()))
+ .collect(),
+ always_allow_tool_actions: agent.always_allow_tool_actions.unwrap(),
+ notify_when_agent_waiting: agent.notify_when_agent_waiting.unwrap(),
+ play_sound_when_agent_done: agent.play_sound_when_agent_done.unwrap(),
+ stream_edits: agent.stream_edits.unwrap(),
+ single_file_review: agent.single_file_review.unwrap(),
+ model_parameters: agent.model_parameters,
+ preferred_completion_mode: agent.preferred_completion_mode.unwrap().into(),
+ enable_feedback: agent.enable_feedback.unwrap(),
+ expand_edit_card: agent.expand_edit_card.unwrap(),
+ expand_terminal_card: agent.expand_terminal_card.unwrap(),
+ use_modifier_to_send: agent.use_modifier_to_send.unwrap(),
+ }
+ }
- for value in sources.defaults_and_customizations() {
- merge(&mut settings.enabled, value.enabled);
- merge(&mut settings.button, value.button);
- merge(&mut settings.dock, value.dock);
- merge(
- &mut settings.default_width,
- value.default_width.map(Into::into),
- );
- merge(
- &mut settings.default_height,
- value.default_height.map(Into::into),
- );
- settings.default_model = value
- .default_model
- .clone()
- .or(settings.default_model.take());
- settings.inline_assistant_model = value
- .inline_assistant_model
- .clone()
- .or(settings.inline_assistant_model.take());
- settings.commit_message_model = value
- .clone()
- .commit_message_model
- .or(settings.commit_message_model.take());
- settings.thread_summary_model = value
- .clone()
- .thread_summary_model
- .or(settings.thread_summary_model.take());
- merge(
- &mut settings.inline_alternatives,
- value.inline_alternatives.clone(),
- );
- merge(
- &mut settings.notify_when_agent_waiting,
- value.notify_when_agent_waiting,
- );
- merge(
- &mut settings.play_sound_when_agent_done,
- value.play_sound_when_agent_done,
- );
- merge(&mut settings.stream_edits, value.stream_edits);
- merge(&mut settings.single_file_review, value.single_file_review);
- merge(&mut settings.default_profile, value.default_profile.clone());
- merge(&mut settings.default_view, value.default_view);
- merge(
- &mut settings.preferred_completion_mode,
- value.preferred_completion_mode,
- );
- merge(&mut settings.enable_feedback, value.enable_feedback);
- merge(&mut settings.expand_edit_card, value.expand_edit_card);
- merge(
- &mut settings.expand_terminal_card,
- value.expand_terminal_card,
- );
- merge(
- &mut settings.use_modifier_to_send,
- value.use_modifier_to_send,
+ fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
+ let Some(value) = &content.agent else { return };
+ self.enabled.merge_from(&value.enabled);
+ self.button.merge_from(&value.button);
+ self.dock.merge_from(&value.dock);
+ self.default_width
+ .merge_from(&value.default_width.map(Into::into));
+ self.default_height
+ .merge_from(&value.default_height.map(Into::into));
+ self.default_model = value.default_model.clone().or(self.default_model.take());
+
+ self.inline_assistant_model = value
+ .inline_assistant_model
+ .clone()
+ .or(self.inline_assistant_model.take());
+ self.commit_message_model = value
+ .clone()
+ .commit_message_model
+ .or(self.commit_message_model.take());
+ self.thread_summary_model = value
+ .clone()
+ .thread_summary_model
+ .or(self.thread_summary_model.take());
+ self.inline_alternatives
+ .merge_from(&value.inline_alternatives.clone());
+ self.notify_when_agent_waiting
+ .merge_from(&value.notify_when_agent_waiting);
+ self.play_sound_when_agent_done
+ .merge_from(&value.play_sound_when_agent_done);
+ self.stream_edits.merge_from(&value.stream_edits);
+ self.single_file_review
+ .merge_from(&value.single_file_review);
+ self.default_profile
+ .merge_from(&value.default_profile.clone().map(AgentProfileId));
+ self.default_view.merge_from(&value.default_view);
+ self.preferred_completion_mode
+ .merge_from(&value.preferred_completion_mode.map(Into::into));
+ self.enable_feedback.merge_from(&value.enable_feedback);
+ self.expand_edit_card.merge_from(&value.expand_edit_card);
+ self.expand_terminal_card
+ .merge_from(&value.expand_terminal_card);
+ self.use_modifier_to_send
+ .merge_from(&value.use_modifier_to_send);
+
+ self.model_parameters
+ .extend_from_slice(&value.model_parameters);
+
+ if let Some(profiles) = value.profiles.as_ref() {
+ self.profiles.extend(
+ profiles
+ .into_iter()
+ .map(|(id, profile)| (AgentProfileId(id.clone()), profile.clone().into())),
);
-
- settings
- .model_parameters
- .extend_from_slice(&value.model_parameters);
-
- if let Some(profiles) = value.profiles.as_ref() {
- settings
- .profiles
- .extend(profiles.into_iter().map(|(id, profile)| {
- (
- id.clone(),
- AgentProfileSettings {
- name: profile.name.clone().into(),
- tools: profile.tools.clone(),
- enable_all_context_servers: profile
- .enable_all_context_servers
- .unwrap_or_default(),
- context_servers: profile
- .context_servers
- .iter()
- .map(|(context_server_id, preset)| {
- (
- context_server_id.clone(),
- ContextServerPreset {
- tools: preset.tools.clone(),
- },
- )
- })
- .collect(),
- },
- )
- }));
- }
}
-
- debug_assert!(
- !sources.default.always_allow_tool_actions.unwrap_or(false),
- "For security, agent.always_allow_tool_actions should always be false in default.json. If it's true, that is a bug that should be fixed!"
- );
-
- // For security reasons, only trust the user's global settings for whether to always allow tool actions.
- // If this could be overridden locally, an attacker could (e.g. by committing to source control and
- // convincing you to switch branches) modify your project-local settings to disable the agent's safety checks.
- settings.always_allow_tool_actions = sources
- .user
- .and_then(|setting| setting.always_allow_tool_actions)
- .unwrap_or(false);
-
- Ok(settings)
}
- fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
+ fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
if let Some(b) = vscode
.read_value("chat.agent.enabled")
.and_then(|b| b.as_bool())
{
- current.enabled = Some(b);
- current.button = Some(b);
+ current.agent.get_or_insert_default().enabled = Some(b);
+ current.agent.get_or_insert_default().button = Some(b);
}
}
}
-
-fn merge<T>(target: &mut T, value: Option<T>) {
- if let Some(value) = value {
- *target = value;
- }
-}