use crate::error::AgentConfigError;
use crate::integration::{InstallReport, UninstallReport};
use crate::plan::{InstallPlan, PlanTarget, RefusalReason, UninstallPlan};
use crate::scope::Scope;
use crate::spec::{HookSpec, InstructionSpec};
use crate::status::{ConfigPresence, StatusReport};
use crate::util::{fs_atomic, md_block, ownership};
use super::{
install::install, ledger_path, paths_for_status, plan::plan_install, uninstall::plan_uninstall,
uninstall::uninstall, InlineLayout, StandaloneLayout,
};
pub(crate) fn inline_status(
layout: InlineLayout,
name: &str,
expected_owner: &str,
) -> Result<StatusReport, AgentConfigError> {
InstructionSpec::validate_name(name)?;
let led = ledger_path(&layout.config_dir);
let content = fs_atomic::read_to_string_or_empty(&layout.host_file)?;
let block_in_host = md_block::contains_instruction(&content, name)
|| md_block::contains_legacy_instruction(&content, name);
let presence = if block_in_host {
ConfigPresence::Single
} else {
ConfigPresence::Absent
};
let recorded = ownership::owner_of(&led, name)?;
Ok(StatusReport::for_instruction(
name,
layout.host_file,
led,
presence,
expected_owner,
recorded,
))
}
pub(crate) fn inline_plan_install(
integration_id: &'static str,
scope: &Scope,
layout: Result<InlineLayout, AgentConfigError>,
spec: &InstructionSpec,
) -> Result<InstallPlan, AgentConfigError> {
spec.validate()?;
let target = PlanTarget::Instruction {
integration_id,
scope: scope.clone(),
name: spec.name.clone(),
owner: spec.owner_tag.clone(),
};
let layout = match layout {
Ok(l) => l,
Err(AgentConfigError::UnsupportedScope { .. }) => {
return Ok(InstallPlan::refused(
target,
None,
RefusalReason::UnsupportedScope,
));
}
Err(e) => return Err(e),
};
let changes = plan_install(
&layout.config_dir,
spec,
Some(&layout.host_file),
None,
None,
)?;
Ok(InstallPlan::from_changes(target, changes))
}
pub(crate) fn inline_plan_uninstall(
integration_id: &'static str,
scope: &Scope,
layout: Result<InlineLayout, AgentConfigError>,
name: &str,
owner_tag: &str,
) -> Result<UninstallPlan, AgentConfigError> {
InstructionSpec::validate_name(name)?;
HookSpec::validate_tag(owner_tag)?;
let target = PlanTarget::Instruction {
integration_id,
scope: scope.clone(),
name: name.to_string(),
owner: owner_tag.to_string(),
};
let layout = match layout {
Ok(l) => l,
Err(AgentConfigError::UnsupportedScope { .. }) => {
return Ok(UninstallPlan::refused(
target,
None,
RefusalReason::UnsupportedScope,
));
}
Err(e) => return Err(e),
};
let changes = plan_uninstall(
&layout.config_dir,
name,
owner_tag,
Some(&layout.host_file),
None,
)?;
Ok(UninstallPlan::from_changes(target, changes))
}
pub(crate) fn inline_install(
scope: &Scope,
layout: InlineLayout,
spec: &InstructionSpec,
) -> Result<InstallReport, AgentConfigError> {
spec.validate()?;
scope.ensure_contained(&layout.host_file)?;
install(
scope,
&layout.config_dir,
spec,
Some(&layout.host_file),
None,
None,
)
}
pub(crate) fn inline_uninstall(
scope: &Scope,
layout: InlineLayout,
name: &str,
owner_tag: &str,
) -> Result<UninstallReport, AgentConfigError> {
InstructionSpec::validate_name(name)?;
HookSpec::validate_tag(owner_tag)?;
scope.ensure_contained(&layout.host_file)?;
uninstall(
scope,
&layout.config_dir,
name,
owner_tag,
Some(&layout.host_file),
None,
)
}
pub(crate) fn standalone_status(
layout: StandaloneLayout,
name: &str,
expected_owner: &str,
) -> Result<StatusReport, AgentConfigError> {
InstructionSpec::validate_name(name)?;
let (instr_path, led) = paths_for_status(&layout.config_dir, &layout.instruction_dir, name);
let presence = if instr_path.exists() {
ConfigPresence::Single
} else {
ConfigPresence::Absent
};
let recorded = ownership::owner_of(&led, name)?;
Ok(StatusReport::for_instruction(
name,
instr_path,
led,
presence,
expected_owner,
recorded,
))
}
pub(crate) fn standalone_plan_install(
integration_id: &'static str,
scope: &Scope,
layout: Result<StandaloneLayout, AgentConfigError>,
spec: &InstructionSpec,
) -> Result<InstallPlan, AgentConfigError> {
spec.validate()?;
let target = PlanTarget::Instruction {
integration_id,
scope: scope.clone(),
name: spec.name.clone(),
owner: spec.owner_tag.clone(),
};
let layout = match layout {
Ok(l) => l,
Err(AgentConfigError::UnsupportedScope { .. }) => {
return Ok(InstallPlan::refused(
target,
None,
RefusalReason::UnsupportedScope,
));
}
Err(e) => return Err(e),
};
let changes = plan_install(
&layout.config_dir,
spec,
None,
Some(&layout.instruction_dir),
None,
)?;
Ok(InstallPlan::from_changes(target, changes))
}
pub(crate) fn standalone_plan_uninstall(
integration_id: &'static str,
scope: &Scope,
layout: Result<StandaloneLayout, AgentConfigError>,
name: &str,
owner_tag: &str,
) -> Result<UninstallPlan, AgentConfigError> {
InstructionSpec::validate_name(name)?;
HookSpec::validate_tag(owner_tag)?;
let target = PlanTarget::Instruction {
integration_id,
scope: scope.clone(),
name: name.to_string(),
owner: owner_tag.to_string(),
};
let layout = match layout {
Ok(l) => l,
Err(AgentConfigError::UnsupportedScope { .. }) => {
return Ok(UninstallPlan::refused(
target,
None,
RefusalReason::UnsupportedScope,
));
}
Err(e) => return Err(e),
};
let changes = plan_uninstall(
&layout.config_dir,
name,
owner_tag,
None,
Some(&layout.instruction_dir),
)?;
Ok(UninstallPlan::from_changes(target, changes))
}
pub(crate) fn standalone_install(
scope: &Scope,
layout: StandaloneLayout,
spec: &InstructionSpec,
) -> Result<InstallReport, AgentConfigError> {
spec.validate()?;
let target_file = layout.instruction_dir.join(format!("{}.md", spec.name));
scope.ensure_contained(&target_file)?;
install(
scope,
&layout.config_dir,
spec,
None,
Some(&layout.instruction_dir),
None,
)
}
pub(crate) fn standalone_uninstall(
scope: &Scope,
layout: StandaloneLayout,
name: &str,
owner_tag: &str,
) -> Result<UninstallReport, AgentConfigError> {
InstructionSpec::validate_name(name)?;
HookSpec::validate_tag(owner_tag)?;
scope.ensure_contained(&layout.instruction_dir)?;
uninstall(
scope,
&layout.config_dir,
name,
owner_tag,
None,
Some(&layout.instruction_dir),
)
}