aboutsummaryrefslogtreecommitdiffstats
path: root/sys/nixpkgs/pkgs/generate_moz_extension/src
diff options
context:
space:
mode:
Diffstat (limited to 'sys/nixpkgs/pkgs/generate_moz_extension/src')
-rw-r--r--sys/nixpkgs/pkgs/generate_moz_extension/src/main.rs138
-rw-r--r--sys/nixpkgs/pkgs/generate_moz_extension/src/types.rs71
2 files changed, 209 insertions, 0 deletions
diff --git a/sys/nixpkgs/pkgs/generate_moz_extension/src/main.rs b/sys/nixpkgs/pkgs/generate_moz_extension/src/main.rs
new file mode 100644
index 00000000..bde986a3
--- /dev/null
+++ b/sys/nixpkgs/pkgs/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,
+ })
+}
diff --git a/sys/nixpkgs/pkgs/generate_moz_extension/src/types.rs b/sys/nixpkgs/pkgs/generate_moz_extension/src/types.rs
new file mode 100644
index 00000000..b830fe0d
--- /dev/null
+++ b/sys/nixpkgs/pkgs/generate_moz_extension/src/types.rs
@@ -0,0 +1,71 @@
+use std::fmt::Display;
+
+use anyhow::anyhow;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize)]
+#[allow(non_snake_case)]
+pub struct Extension {
+ pub pname: String,
+ pub default_area: DefaultArea,
+ pub version: String,
+ pub addonId: String,
+ pub url: String,
+ pub sha256: String,
+}
+
+#[derive(Debug, Clone)]
+pub struct InputExtension {
+ pub moz_name: String,
+ pub default_area: DefaultArea,
+}
+#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
+#[allow(non_camel_case_types)]
+pub enum DefaultArea {
+ navbar,
+ menupanel,
+}
+
+impl Display for DefaultArea {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ DefaultArea::navbar => f.write_str("navbar"),
+ DefaultArea::menupanel => f.write_str("menupanel"),
+ }
+ }
+}
+
+impl TryFrom<&str> for DefaultArea {
+ type Error = anyhow::Error;
+
+ fn try_from(value: &str) -> Result<Self, Self::Error> {
+ match value {
+ "navbar" => Ok(Self::navbar),
+ "menupanel" => Ok(Self::menupanel),
+ _ => Err(anyhow!(
+ "Your <default_area> needs to be one of 'navbar' or 'menupanel', but is: '{}'",
+ value
+ )),
+ }
+ }
+}
+
+impl Display for InputExtension {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(&self.moz_name)
+ }
+}
+impl TryFrom<String> for InputExtension {
+ type Error = anyhow::Error;
+
+ fn try_from(value: String) -> Result<Self, Self::Error> {
+ if let Some((moz_name, default_area)) = value.split_once(':') {
+ Ok(Self {
+ moz_name: moz_name.to_owned(),
+ default_area: default_area.try_into()?,
+ })
+ } else {
+ Err(anyhow!("Can't parse the input string as a InputExtension!\n Needs to be: '<moz_name>:<default_area>'"))
+ }
+ }
+}