extension_manifest.rs

  1use anyhow::{anyhow, Context, Result};
  2use collections::BTreeMap;
  3use fs::Fs;
  4use language::LanguageServerName;
  5use serde::{Deserialize, Serialize};
  6use std::{
  7    ffi::OsStr,
  8    path::{Path, PathBuf},
  9    sync::Arc,
 10};
 11use util::SemanticVersion;
 12
 13/// This is the old version of the extension manifest, from when it was `extension.json`.
 14#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
 15pub struct OldExtensionManifest {
 16    pub name: String,
 17    pub version: Arc<str>,
 18
 19    #[serde(default)]
 20    pub description: Option<String>,
 21    #[serde(default)]
 22    pub repository: Option<String>,
 23    #[serde(default)]
 24    pub authors: Vec<String>,
 25
 26    #[serde(default)]
 27    pub themes: BTreeMap<Arc<str>, PathBuf>,
 28    #[serde(default)]
 29    pub languages: BTreeMap<Arc<str>, PathBuf>,
 30    #[serde(default)]
 31    pub grammars: BTreeMap<Arc<str>, PathBuf>,
 32}
 33
 34#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
 35pub struct ExtensionManifest {
 36    pub id: Arc<str>,
 37    pub name: String,
 38    pub version: Arc<str>,
 39    pub schema_version: i32,
 40
 41    #[serde(default)]
 42    pub description: Option<String>,
 43    #[serde(default)]
 44    pub repository: Option<String>,
 45    #[serde(default)]
 46    pub authors: Vec<String>,
 47    #[serde(default)]
 48    pub lib: LibManifestEntry,
 49
 50    #[serde(default)]
 51    pub themes: Vec<PathBuf>,
 52    #[serde(default)]
 53    pub languages: Vec<PathBuf>,
 54    #[serde(default)]
 55    pub grammars: BTreeMap<Arc<str>, GrammarManifestEntry>,
 56    #[serde(default)]
 57    pub language_servers: BTreeMap<LanguageServerName, LanguageServerManifestEntry>,
 58}
 59
 60#[derive(Clone, Default, PartialEq, Eq, Debug, Deserialize, Serialize)]
 61pub struct LibManifestEntry {
 62    pub kind: Option<ExtensionLibraryKind>,
 63    pub version: Option<SemanticVersion>,
 64}
 65
 66#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
 67pub enum ExtensionLibraryKind {
 68    Rust,
 69}
 70
 71#[derive(Clone, Default, PartialEq, Eq, Debug, Deserialize, Serialize)]
 72pub struct GrammarManifestEntry {
 73    pub repository: String,
 74    #[serde(alias = "commit")]
 75    pub rev: String,
 76}
 77
 78#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
 79pub struct LanguageServerManifestEntry {
 80    pub language: Arc<str>,
 81}
 82
 83impl ExtensionManifest {
 84    pub async fn load(fs: Arc<dyn Fs>, extension_dir: &Path) -> Result<Self> {
 85        let extension_name = extension_dir
 86            .file_name()
 87            .and_then(OsStr::to_str)
 88            .ok_or_else(|| anyhow!("invalid extension name"))?;
 89
 90        let mut extension_manifest_path = extension_dir.join("extension.json");
 91        if fs.is_file(&extension_manifest_path).await {
 92            let manifest_content = fs
 93                .load(&extension_manifest_path)
 94                .await
 95                .with_context(|| format!("failed to load {extension_name} extension.json"))?;
 96            let manifest_json = serde_json::from_str::<OldExtensionManifest>(&manifest_content)
 97                .with_context(|| {
 98                    format!("invalid extension.json for extension {extension_name}")
 99                })?;
100
101            Ok(manifest_from_old_manifest(manifest_json, extension_name))
102        } else {
103            extension_manifest_path.set_extension("toml");
104            let manifest_content = fs
105                .load(&extension_manifest_path)
106                .await
107                .with_context(|| format!("failed to load {extension_name} extension.toml"))?;
108            toml::from_str(&manifest_content)
109                .with_context(|| format!("invalid extension.json for extension {extension_name}"))
110        }
111    }
112}
113
114fn manifest_from_old_manifest(
115    manifest_json: OldExtensionManifest,
116    extension_id: &str,
117) -> ExtensionManifest {
118    ExtensionManifest {
119        id: extension_id.into(),
120        name: manifest_json.name,
121        version: manifest_json.version,
122        description: manifest_json.description,
123        repository: manifest_json.repository,
124        authors: manifest_json.authors,
125        schema_version: 0,
126        lib: Default::default(),
127        themes: {
128            let mut themes = manifest_json.themes.into_values().collect::<Vec<_>>();
129            themes.sort();
130            themes.dedup();
131            themes
132        },
133        languages: {
134            let mut languages = manifest_json.languages.into_values().collect::<Vec<_>>();
135            languages.sort();
136            languages.dedup();
137            languages
138        },
139        grammars: manifest_json
140            .grammars
141            .into_keys()
142            .map(|grammar_name| (grammar_name, Default::default()))
143            .collect(),
144        language_servers: Default::default(),
145    }
146}