Skip to main content

cli/sys/
profile_commands.rs

1use anyhow::{Context, Result, bail};
2
3use crate::colors;
4use crate::config::Config;
5
6use super::bootstrap::item_is_present;
7use super::commands::current_unix_timestamp;
8use super::detect::detect_os_id;
9use super::manifest::load_sys_preset;
10use super::profile::install_sys_profile_loader_with_templates;
11use super::profile_compose::{compose_sys_profiles, enabled_profile_items};
12use super::run_manifest::{SysRunEntry, SysRunManifest};
13use super::{SysItemMode, SysItemStatus};
14
15pub async fn handle_profile_enable(config: &Config, item_id: &str, dry_run: bool) -> Result<()> {
16    handle_profile_state(config, item_id, true, dry_run).await
17}
18
19pub async fn handle_profile_disable(config: &Config, item_id: &str, dry_run: bool) -> Result<()> {
20    handle_profile_state(config, item_id, false, dry_run).await
21}
22
23pub(super) async fn sync_composed_profile(
24    config: &Config,
25) -> Result<Option<super::SysItemOutcome>> {
26    let os_id = detect_os_id().await?;
27    let loaded = load_sys_preset(config, &os_id).await?;
28    let run_manifest = SysRunManifest::load(config.shine_dir()).await?;
29    let enabled_items = enabled_profile_items(&loaded.manifest, &run_manifest.entries, &os_id);
30    let sys_shell: &'static str = config.shell_type.into();
31    let templates =
32        compose_sys_profiles(config, &os_id, &loaded, &enabled_items, sys_shell).await?;
33    install_sys_profile_loader_with_templates(
34        config,
35        &os_id,
36        &loaded.root,
37        sys_shell,
38        false,
39        Some(&templates),
40    )
41    .await
42    .map(Some)
43}
44
45async fn handle_profile_state(
46    config: &Config,
47    item_id: &str,
48    enabled: bool,
49    dry_run: bool,
50) -> Result<()> {
51    crate::config::print_presets_note(config);
52    let os_id = detect_os_id().await?;
53    let loaded = load_sys_preset(config, &os_id).await?;
54    let item = loaded
55        .manifest
56        .items
57        .iter()
58        .find(|item| item.id == item_id)
59        .with_context(|| format!("unknown sys item `{item_id}` for {os_id}"))?;
60    if item.mode != SysItemMode::Init {
61        bail!("managed sys item `{item_id}` has no bootstrap shell integration");
62    }
63    if item.shell.is_empty() {
64        bail!("sys item `{item_id}` declares no shell integration");
65    }
66    if enabled && !item_is_present(config, item).await? {
67        bail!(
68            "sys item `{item_id}` is not currently detected; run `shine sys bootstrap {item_id}` first"
69        );
70    }
71
72    let mut run_manifest = SysRunManifest::load(config.shine_dir()).await?;
73    let existing = run_manifest
74        .entries
75        .iter_mut()
76        .find(|entry| entry.os_id == os_id && entry.item_id == item_id && !entry.managed);
77    match existing {
78        Some(entry) => entry.profile_enabled = enabled,
79        None if enabled => run_manifest.upsert(SysRunEntry {
80            os_id: os_id.clone(),
81            item_id: item.id.clone(),
82            label: item.label.clone(),
83            status: SysItemStatus::AlreadyInstalled,
84            detail: "shell integration enabled after live detection".to_string(),
85            updated_at: current_unix_timestamp().to_string(),
86            managed: false,
87            profile_enabled: true,
88            receipt: None,
89        }),
90        None => {}
91    }
92
93    let enabled_items = enabled_profile_items(&loaded.manifest, &run_manifest.entries, &os_id);
94    let sys_shell: &'static str = config.shell_type.into();
95    let templates =
96        compose_sys_profiles(config, &os_id, &loaded, &enabled_items, sys_shell).await?;
97    let action = if enabled { "enable" } else { "disable" };
98    if dry_run {
99        println!(
100            "{} sys/{item_id}: would {action} shell integration for {sys_shell}",
101            colors::dim("[dry-run]")
102        );
103        return Ok(());
104    }
105
106    let outcome = install_sys_profile_loader_with_templates(
107        config,
108        &os_id,
109        &loaded.root,
110        sys_shell,
111        false,
112        Some(&templates),
113    )
114    .await?;
115    run_manifest.save(config.shine_dir()).await?;
116    println!(
117        "{} sys/{item_id}: shell integration {} ({})",
118        colors::symbol("✓"),
119        if enabled { "enabled" } else { "disabled" },
120        outcome.detail
121    );
122    Ok(())
123}