diff options
Diffstat (limited to 'pkgs/by-name/ge/generate_moz_extension/src/main.rs')
-rw-r--r-- | pkgs/by-name/ge/generate_moz_extension/src/main.rs | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/pkgs/by-name/ge/generate_moz_extension/src/main.rs b/pkgs/by-name/ge/generate_moz_extension/src/main.rs new file mode 100644 index 00000000..bde986a3 --- /dev/null +++ b/pkgs/by-name/ge/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, + }) +} |