Add nix package search
This time via a local json that’s lazily initialized because they don’t seem to have a proper API
This commit is contained in:
parent
11bb12bcee
commit
8157ab760d
|
@ -7,7 +7,7 @@ pub fn query_apt(ctx: Context, msg: Message, args: Vec<&str>) {
|
||||||
let query = args.join(" ");
|
let query = args.join(" ");
|
||||||
let response: Response = search(
|
let response: Response = search(
|
||||||
&format!("https://sources.debian.org/api/src/{}/", &query),
|
&format!("https://sources.debian.org/api/src/{}/", &query),
|
||||||
EMPTY_RESULT,
|
|_e| EMPTY_RESULT,
|
||||||
);
|
);
|
||||||
if response.versions.len() == 0 {
|
if response.versions.len() == 0 {
|
||||||
send(msg.channel_id, "No results", &ctx);
|
send(msg.channel_id, "No results", &ctx);
|
||||||
|
|
|
@ -5,6 +5,7 @@ use serde::de::DeserializeOwned;
|
||||||
use serenity::prelude::*;
|
use serenity::prelude::*;
|
||||||
mod apt;
|
mod apt;
|
||||||
mod pacman;
|
mod pacman;
|
||||||
|
mod nix;
|
||||||
extern crate reqwest;
|
extern crate reqwest;
|
||||||
|
|
||||||
pub struct Handler;
|
pub struct Handler;
|
||||||
|
@ -49,6 +50,7 @@ lazy_static! {
|
||||||
let mut command_list = Vec::new();
|
let mut command_list = Vec::new();
|
||||||
command_list.push(Command::new("pacman", pacman::query_pacman));
|
command_list.push(Command::new("pacman", pacman::query_pacman));
|
||||||
command_list.push(Command::new("apt", apt::query_apt));
|
command_list.push(Command::new("apt", apt::query_apt));
|
||||||
|
command_list.push(Command::new("nix", nix::query_nix));
|
||||||
command_list
|
command_list
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -85,8 +87,8 @@ pub fn respond_with_results<T: fmt::Display>(target: ChannelId, results: &Vec<T>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search<T: DeserializeOwned>(url: &str, fallback: T) -> T {
|
pub fn search<T: DeserializeOwned>(url: &str, fallback: impl Fn(reqwest::Error) -> T) -> T {
|
||||||
return search_inner(url).unwrap_or(fallback);
|
return search_inner(url).unwrap_or_else(fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_inner<T: DeserializeOwned>(url: &str) -> Result<T, reqwest::Error> {
|
fn search_inner<T: DeserializeOwned>(url: &str) -> Result<T, reqwest::Error> {
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
use crate::commands::*;
|
||||||
|
use serde::de;
|
||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
|
use serenity::model::channel::Message;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub fn query_nix(ctx: Context, msg: Message, args: Vec<&str>) {
|
||||||
|
// we know for sure that there’s at least one element here
|
||||||
|
let query = args[0];
|
||||||
|
match NIX_PACKAGES.packages.get(query) {
|
||||||
|
Some(result) => respond_with_results(msg.channel_id, &vec![result], &ctx),
|
||||||
|
None => send(msg.channel_id, "No results", &ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
#[derive(Debug)]
|
||||||
|
static ref NIX_PACKAGES: Response = {
|
||||||
|
search(
|
||||||
|
&"https://nixos.org/nixpkgs/packages-nixos-19.09.json",
|
||||||
|
|e| { panic!("{}", e)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct Response {
|
||||||
|
pub commit: String,
|
||||||
|
pub packages: HashMap<String, Package>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct Package {
|
||||||
|
pname: String,
|
||||||
|
version: String,
|
||||||
|
meta: PackageMeta,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct PackageMeta {
|
||||||
|
description: Option<String>,
|
||||||
|
/**
|
||||||
|
* It appears as though the Nix repositories allow either no homepage, one homepage, or a list.
|
||||||
|
* Definitely no unnecessary complexity because of that.
|
||||||
|
*/
|
||||||
|
#[serde(default)]
|
||||||
|
#[serde(deserialize_with = "string_or_seq_string")]
|
||||||
|
homepage: String,
|
||||||
|
//homepage: Option<Homepage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Package {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
// This is an en-dash, not a minus sign.
|
||||||
|
// Putting a minus here removes the first line from output,
|
||||||
|
// but only for some packages. en-dash works fine.
|
||||||
|
"{}–{}\n {}\n Homepage: {}",
|
||||||
|
self.pname,
|
||||||
|
self.version,
|
||||||
|
match &self.meta.description {
|
||||||
|
Some(description) => description,
|
||||||
|
None => "No description provided",
|
||||||
|
},
|
||||||
|
self.meta.homepage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom deserializer to handle the string/vec<string> homepage field.
|
||||||
|
// Heavily inspired by
|
||||||
|
// https://stackoverflow.com/questions/41151080/deserialize-a-json-string-or-array-of-strings-into-a-vec
|
||||||
|
fn string_or_seq_string<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct StringOrVec(PhantomData<String>);
|
||||||
|
|
||||||
|
impl<'de> de::Visitor<'de> for StringOrVec {
|
||||||
|
type Value = String;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("string or list of strings")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
|
||||||
|
Ok(value.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
|
||||||
|
Ok("unspecified".to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_seq<S: de::SeqAccess<'de>>(self, visitor: S) -> Result<Self::Value, S::Error> {
|
||||||
|
let strings: Vec<String> =
|
||||||
|
Deserialize::deserialize(de::value::SeqAccessDeserializer::new(visitor))?;
|
||||||
|
Ok(strings.into_iter().next().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(StringOrVec(PhantomData))
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ pub fn query_pacman(ctx: Context, msg: Message, args: Vec<&str>) {
|
||||||
"https://www.archlinux.org/packages/search/json/?name={}",
|
"https://www.archlinux.org/packages/search/json/?name={}",
|
||||||
&args[0]
|
&args[0]
|
||||||
),
|
),
|
||||||
EMPTY_RESULT,
|
|_e| EMPTY_RESULT,
|
||||||
);
|
);
|
||||||
// this is 1 for most packages and 2 if there’s a second version in testing
|
// this is 1 for most packages and 2 if there’s a second version in testing
|
||||||
if response.results.len() != 0 {
|
if response.results.len() != 0 {
|
||||||
|
@ -25,7 +25,7 @@ pub fn query_pacman(ctx: Context, msg: Message, args: Vec<&str>) {
|
||||||
"https://www.archlinux.org/packages/search/json/?q={}",
|
"https://www.archlinux.org/packages/search/json/?q={}",
|
||||||
&query
|
&query
|
||||||
),
|
),
|
||||||
EMPTY_RESULT,
|
|_e| EMPTY_RESULT,
|
||||||
);
|
);
|
||||||
if response.results.len() == 0 {
|
if response.results.len() == 0 {
|
||||||
send(msg.channel_id, "No results", &ctx);
|
send(msg.channel_id, "No results", &ctx);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user