|
1 | | -// `fsn store` — browse and manage the FreeSynergy module store. |
| 1 | +// `fsn store` — browse and manage the FreeSynergy module store and language packs. |
2 | 2 | // |
3 | 3 | // Uses StoreClient (fsn-store) to fetch the Node.Store catalog and display |
4 | 4 | // available modules. Install/update are delegated to `fsn deploy` once |
|
7 | 7 | use anyhow::{Context, Result}; |
8 | 8 | use fsn_node_core::store::StoreEntry; |
9 | 9 | use fsn_store::StoreClient; |
| 10 | +use toml; |
| 11 | + |
| 12 | +// Schema version bundled with this binary — must match Lib's sync_snippets.py SCHEMA_VERSION. |
| 13 | +const BUNDLED_SCHEMA_VERSION: &str = "1.0.0"; |
10 | 14 |
|
11 | 15 | // ── Catalog helper ───────────────────────────────────────────────────────────── |
12 | 16 |
|
@@ -116,3 +120,65 @@ pub async fn update_check() -> Result<()> { |
116 | 120 | println!("To update a deployed module, run `fsn update --service <name>`."); |
117 | 121 | Ok(()) |
118 | 122 | } |
| 123 | + |
| 124 | +// ── i18n ─────────────────────────────────────────────────────────────────────── |
| 125 | + |
| 126 | +/// One entry from `[[languages]]` in the store's `catalog/i18n.toml`. |
| 127 | +#[derive(serde::Deserialize)] |
| 128 | +struct I18nCatalogEntry { |
| 129 | + code: String, |
| 130 | + name: String, |
| 131 | + #[serde(default)] |
| 132 | + completeness: u8, |
| 133 | + #[serde(default)] |
| 134 | + schema_version: String, |
| 135 | + // file / sha256 / size are used by the updater, not displayed here |
| 136 | +} |
| 137 | + |
| 138 | +#[derive(serde::Deserialize)] |
| 139 | +struct I18nCatalog { |
| 140 | + #[serde(default, rename = "languages")] |
| 141 | + languages: Vec<I18nCatalogEntry>, |
| 142 | +} |
| 143 | + |
| 144 | +/// Show all available language packs from the store catalog with completeness. |
| 145 | +pub async fn i18n_status() -> Result<()> { |
| 146 | + let client = StoreClient::node_store(); |
| 147 | + let raw = client |
| 148 | + .fetch_raw("Node/catalog/i18n.toml") |
| 149 | + .await |
| 150 | + .context("fetching i18n catalog")?; |
| 151 | + let catalog: I18nCatalog = toml::from_str(&raw).context("parsing i18n catalog")?; |
| 152 | + |
| 153 | + if catalog.languages.is_empty() { |
| 154 | + println!("No language packs available in the store yet."); |
| 155 | + return Ok(()); |
| 156 | + } |
| 157 | + |
| 158 | + println!("{:<6} {:<24} {:>5} {:<8} {}", "CODE", "LANGUAGE", "COMP%", "SCHEMA", ""); |
| 159 | + println!("{}", "─".repeat(58)); |
| 160 | + for e in &catalog.languages { |
| 161 | + let ok = e.schema_version.is_empty() || e.schema_version == BUNDLED_SCHEMA_VERSION; |
| 162 | + let marker = if ok { "✓" } else { "⚠ outdated" }; |
| 163 | + let schema = if e.schema_version.is_empty() { BUNDLED_SCHEMA_VERSION } else { &e.schema_version }; |
| 164 | + println!("{:<6} {:<24} {:>4}% {:<8} {}", e.code, e.name, e.completeness, schema, marker); |
| 165 | + } |
| 166 | + println!("\nRequired schema: {BUNDLED_SCHEMA_VERSION} (⚠ = needs update, run `fsn store i18n set <code>`)"); |
| 167 | + Ok(()) |
| 168 | +} |
| 169 | + |
| 170 | +/// Download and activate a language pack. |
| 171 | +pub async fn i18n_set(lang: &str) -> Result<()> { |
| 172 | + // TODO: fetch merged lang file from store → write to ~/.config/fsn/i18n/{lang}.toml → set active |
| 173 | + println!("Language pack '{lang}' — download and activation not yet implemented."); |
| 174 | + println!("Run `fsn store i18n status` to see available packs."); |
| 175 | + Ok(()) |
| 176 | +} |
| 177 | + |
| 178 | +/// Check whether the currently active language pack matches the bundled schema version. |
| 179 | +pub async fn i18n_check() -> Result<()> { |
| 180 | + // TODO: read active lang from ~/.config/fsn/config.toml, compare schema_version |
| 181 | + println!("Schema version (bundled): {BUNDLED_SCHEMA_VERSION}"); |
| 182 | + println!("Active language check — not yet implemented."); |
| 183 | + Ok(()) |
| 184 | +} |
0 commit comments