aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/sources/generate_moz_extension/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/sources/generate_moz_extension/src/main.rs')
-rw-r--r--pkgs/sources/generate_moz_extension/src/main.rs138
1 files changed, 138 insertions, 0 deletions
diff --git a/pkgs/sources/generate_moz_extension/src/main.rs b/pkgs/sources/generate_moz_extension/src/main.rs
new file mode 100644
index 00000000..bde986a3
--- /dev/null
+++ b/pkgs/sources/generate_moz_extension/src/main.rs
@@ -0,0 +1,138 @@
+use std::env::args;
+
+use anyhow::{bail, Context};
+use futures::StreamExt;
+use reqwest::Client;
+use serde_json::{json, Map, Value};
+
+pub mod types;
+
+macro_rules! get_json_value {
+ ($key:expr, $json_value:ident, $type:ident, $get:ident) => {
+ match $json_value.get($key) {
+ Some(resp) => {
+ let resp = resp.to_owned();
+ if resp.$type() {
+ resp.$get().expect(
+ "The should have been checked in the if guard, so unpacking here is fine",
+ ).to_owned()
+ } else {
+ bail!(
+ "Value {} => \n{}\n is not of type: {}",
+ $key,
+ resp,
+ stringify!($type)
+ );
+ }
+ }
+ None => {
+ bail!(
+ "There seems to be no '{}' in your json data (json value: '{}')\n Has the api changend?",
+ $key, serde_json::to_string_pretty(&$json_value).expect("Will always work")
+ );
+ }
+ }
+ };
+}
+
+use futures::stream::futures_unordered::FuturesUnordered;
+use types::{Extension, InputExtension};
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ let mut extensions: Vec<InputExtension> = vec![];
+ for input_extension in args()
+ .skip(1)
+ .map(|str| InputExtension::try_from(str))
+ .collect::<Vec<anyhow::Result<InputExtension>>>()
+ {
+ extensions.push(input_extension?);
+ }
+
+ let resulting_extensions = process_extensions(extensions).await?;
+
+ let mut output = Map::new();
+ for extension in resulting_extensions {
+ output.insert(extension.pname.clone(), json!(extension));
+ }
+
+ println!(
+ "{}",
+ serde_json::to_string_pretty(&serde_json::Value::Object(output)).expect(
+ "This is constructed from json, it should also be possible to serialize it again"
+ )
+ );
+ Ok(())
+}
+
+async fn process_extensions(extensions: Vec<InputExtension>) -> anyhow::Result<Vec<Extension>> {
+ let mut output = Vec::with_capacity(extensions.len());
+
+ let client = Client::new();
+ for extension in extensions
+ .iter()
+ .map(|ext| {
+ let local_client = &client;
+ index_extension(ext, local_client)
+ })
+ .collect::<FuturesUnordered<_>>()
+ .collect::<Vec<_>>()
+ .await
+ {
+ output.push(extension?);
+ }
+ Ok(output)
+}
+
+async fn index_extension(extension: &InputExtension, client: &Client) -> anyhow::Result<Extension> {
+ let response = client
+ .get(format!(
+ "https://addons.mozilla.org/api/v5/addons/addon/{}",
+ extension,
+ ))
+ .send()
+ .await
+ .context("Accessing the mozzila extenios api failed with error: {e}")?;
+
+ eprintln!("Indexing {} ({})...", extension, response.status());
+ let response: Value = serde_json::from_str(
+ &response
+ .text()
+ .await
+ .context("Turning the response to text fail with error: {e}")?,
+ )
+ .context("Deserializing the response failed! Error: {e}")?;
+
+ if let Some(detail) = response.get("detail") {
+ if detail == "Not found." {
+ bail!("Your extension ('{}') was not found!", extension);
+ }
+ };
+
+ let release = { get_json_value!("current_version", response, is_object, as_object) };
+
+ #[allow(non_snake_case)]
+ let addonId = { get_json_value!("guid", response, is_string, as_str) };
+
+ let version = { get_json_value!("version", release, is_string, as_str) };
+ let file = { get_json_value!("file", release, is_object, as_object) };
+
+ let url = { get_json_value!("url", file, is_string, as_str) };
+ let sha256 = {
+ let hash = get_json_value!("hash", file, is_string, as_str);
+ if hash.starts_with("sha256:") {
+ hash
+ } else {
+ bail!("This hash type is unhandled: {}", hash);
+ }
+ };
+
+ Ok(Extension {
+ pname: extension.moz_name.clone(),
+ default_area: extension.default_area,
+ version,
+ addonId,
+ url,
+ sha256,
+ })
+}