> For the complete documentation index, see [llms.txt](https://cristiandis.gitbook.io/ntix/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cristiandis.gitbook.io/ntix/ntix-documentation/docs-it/api/package-managers.md).

# Gestori di pacchetti

## Gestori di pacchetti

L'interazione con il gestore di pacchetti si trova in `ntix_rs::package_manager`.

### PackageManager

`models::package_manager::PackageManager` è l'identificatore tipizzato per i gestori supportati, usato in tutta la codebase al posto del vecchio `"winget"` / `"chocolatey"` / `"scoop"` stringhe letterali.

```rust
pub enum PackageManager {
    Winget,      // predefinito
    Chocolatey,
    Scoop,
}

impl PackageManager {
    pub fn as_str(self) -> &'static str;                 // "winget", "chocolatey", "scoop"
    pub fn from_name(s: &str) -> Option<Self>;           // ricerca senza distinzione tra maiuscole e minuscole
    pub fn all() -> [PackageManager; 3];                 // ordine stabile
}
```

### Operazioni per gestore

Ogni gestore ha il proprio modulo con funzioni libere `async fn`che accettano un `&dyn CommandRunner`.

#### `winget_ops`

```rust
pub struct WingetPackageEntry {
    pub id: String,
    pub version: String,
    pub available: Option<String>,
}

pub async fn get_installed_packages(
    runner: &dyn CommandRunner,
) -> Result<HashMap<String, String>, Box<dyn Error + Send + Sync>>;

pub async fn get_upgradable_packages(
    runner: &dyn CommandRunner,
) -> Result<HashMap<String, UpgradeInfo>, Box<dyn Error + Send + Sync>>;

pub async fn package_exists(
    runner: &dyn CommandRunner,
    id: &str,
) -> Result<bool, Box<dyn Error + Send + Sync>>;

pub async fn is_installed(runner: &dyn CommandRunner) -> bool;

pub async fn ensure_installed(runner: &dyn CommandRunner);
```

* `get_installed_packages` / `get_upgradable_packages` analizzare la tabella allineata per colonne `winget list --accept-source-agreements --upgrade` in output.
* `package_exists` esegue un `winget search --id <id> --exact` e segnala se il pacchetto è presente.
* `is_installed` restituisce se winget è nel PATH.
* `ensure_installed` installa automaticamente winget (App Installer) quando manca ed è abilitato.

#### `choco_ops`

```rust
pub async fn is_installed(runner: &dyn CommandRunner) -> bool;

pub async fn get_installed_packages(
    runner: &dyn CommandRunner,
) -> Result<HashMap<String, String>, Box<dyn Error + Send + Sync>>;

pub async fn get_upgradable_packages(runner: &dyn CommandRunner) -> HashMap<String, UpgradeInfo>;

pub async fn package_exists(
    runner: &dyn CommandRunner,
    id: &str,
) -> Result<Option<bool>, Box<dyn Error + Send + Sync>>;
```

`package_exists` restituisce uno stato triplo: `Some(true)` quando il pacchetto viene trovato, `Some(false)` quando `choco search --limit-output` lo segnala inequivocabilmente come assente, e `None` quando l'output della query è inconclusivo (una risposta fallita o inattesa).

#### `scoop_ops`

```rust
pub async fn is_installed(runner: &dyn CommandRunner) -> bool;

pub async fn get_installed_packages(
    runner: &dyn CommandRunner,
) -> Result<HashMap<String, String>, Box<dyn Error + Send + Sync>>;

pub async fn get_upgradable_packages(runner: &dyn CommandRunner) -> HashMap<String, UpgradeInfo>;

pub async fn package_exists(
    runner: &dyn CommandRunner,
    id: &str,
) -> Result<Option<bool>, Box<dyn Error + Send + Sync>>;
```

Come con choco, `package_exists` è a stato triplo: `Some(true)` trovato, `Some(false)` non trovato (`Impossibile trovare il manifest`), `None` quando la risposta non è affidabile.

Rispetto a winget, `get_upgradable_packages` qui restituisce un semplice `HashMap` e degrada a una mappa vuota in caso di query fallita invece di generare un errore.

### CommandRunner

Trait per eseguire comandi shell. Iniettato in diff, execution e detection per garantire testabilità.

```rust
use ntix_rs::package_manager::command_runner::{CommandRunner, LineCallback};

pub type LineCallback<'a> = &'a (dyn Fn(&str) + Sync);

#[async_trait]
pub trait CommandRunner: Send + Sync {
    async fn run(
        &self,
        command: &str,
        on_output: Option<LineCallback<'_>>,
        on_error: Option<LineCallback<'_>>,
    ) -> i32;
    async fn run_output(&self, command: &str, combine_stderr: bool) -> String;
}
```

| Metodo       | Descrizione                                                                                   |
| ------------ | --------------------------------------------------------------------------------------------- |
| `run`        | Esegue un comando; trasmette ogni riga di output ai callback; restituisce il codice di uscita |
| `run_output` | Esegue un comando e cattura stdout (opzionalmente unito a stderr) come stringa                |

`ProcessCommandRunner` è il runner predefinito: invoca `cmd.exe /c <command>` con `CREATE_NO_WINDOW` e trasmette entrambi i pipe riga per riga.

### Rilevamento e validazione

`package_manager_detector` esegue una singola fase di rilevamento delle capacità condivisa da `diff` e `apply`, e valida la disponibilità dei pacchetti.

```rust
pub async fn validate_managers_async(
    options: &NTIXOptions,
    config: &NTIXConfig,
    winget_installed: Option<bool>,
    choco_installed: Option<bool>,
    scoop_installed: Option<bool>,
    runner: Option<&dyn CommandRunner>,
) -> ValidationResult;

pub fn collect_warnings(
    options: &NTIXOptions,
    winget_installed: bool,
    choco_installed: bool,
    scoop_installed: bool,
) -> Vec<String>;

pub async fn get_installed_packages_async(runner: Option<&dyn CommandRunner>) -> InstalledPackages;

pub async fn get_winget_upgradable_packages_async(
    runner: Option<&dyn CommandRunner>,
) -> HashMap<String, UpgradeInfo>;
pub async fn get_choco_upgradable_packages_async(
    runner: Option<&dyn CommandRunner>,
) -> HashMap<String, UpgradeInfo>;
pub async fn get_scoop_upgradable_packages_async(
    runner: Option<&dyn CommandRunner>,
) -> HashMap<String, UpgradeInfo>;

pub async fn validate_winget_packages_exist_async(
    package_ids: &[String],
    runner: Option<&dyn CommandRunner>,
) -> HashMap<String, Option<bool>>;
pub async fn validate_choco_packages_exist_async(
    package_ids: &[String],
    runner: Option<&dyn CommandRunner>,
) -> HashMap<String, Option<bool>>;
pub async fn validate_scoop_packages_exist_async(
    package_ids: &[String],
    runner: Option<&dyn CommandRunner>,
) -> HashMap<String, Option<bool>>;

pub async fn validate_choco_package_exists_async(
    id: &str,
    runner: Option<&dyn CommandRunner>,
) -> Option<bool>;
pub async fn validate_scoop_package_exists_async(
    id: &str,
    runner: Option<&dyn CommandRunner>,
) -> Option<bool>;
```

`ValidationResult` (in `models::manager_validation`) contiene l'esito:

```rust
pub struct ValidationResult {
    pub warnings: Vec<String>,
    pub winget_installed: bool,
    pub choco_installed: bool,
    pub scoop_installed: bool,
}
```

I validatori di esistenza associano a ogni ID di pacchetto `Some(true)` (trovato), `Some(false)` (definitivamente assente), oppure `None` (non è stato possibile verificare). `compute_diff` trasforma `Some(false)` in un `Pacchetto non trovato in <source>` warning e scarta il pacchetto, mentre `None` produce un `Impossibile verificare` warning e mantiene il pacchetto.

### Costruzione dei comandi

`command_builder` costruisce in modo sicuro le stringhe dei comandi shell e valida gli ID dei pacchetti:

```rust
command_builder::validate_id(id)                          // Result<(), Box<dyn Error>>
command_builder::build_winget_install(id, version, opts)
command_builder::build_winget_upgrade(id, opts)
command_builder::build_winget_uninstall(id, opts)
command_builder::build_winget_list()
command_builder::build_winget_search(id)
command_builder::build_winget_version()
command_builder::build_choco_install(id, version, opts)
command_builder::build_choco_upgrade(id, opts)
command_builder::build_choco_uninstall(id, opts)
command_builder::build_choco_search(id)
command_builder::build_choco_version()
command_builder::build_choco_list_installed()
command_builder::build_choco_outdated()
command_builder::build_scoop_install(id, version, opts)
command_builder::build_scoop_upgrade(id, opts)
command_builder::build_scoop_uninstall(id, opts)
command_builder::build_scoop_info(id)
command_builder::build_scoop_version()
command_builder::build_scoop_list_installed()
command_builder::build_scoop_status()
command_builder::build_scoop_bucket_add(name, url)
command_builder::build_scoop_bucket_remove(name)
command_builder::build_scoop_bucket_list()
command_builder::build_powershell_install_winget()
```

`validate_id` rifiuta ID vuoti e qualsiasi ID contenente caratteri al di fuori di `[a-zA-Z0-9._\-/]`, proteggendo contro l'injection nella shell. I builder che interpolano un ID o una versione forniti dall'utente lo chiamano per primi.

### Analisi delle tabelle

`package_manager::table_parser::parse_table` analizza l'output della tabella allineata per colonne, `--`separata da -, prodotto da `winget list` e comandi correlati.

```rust
pub fn parse_table<'a>(lines: impl Iterator<Item = &'a str>) -> Result<(usize, Vec<String>)>
```

Restituisce il numero di colonne dell'intestazione e un vettore piatto di celle trimmate (celle dell'intestazione seguite dalle celle del corpo). Gli offset delle colonne vengono derivati dalla riga di intestazione stessa.
