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}