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:
kageru 2019-10-31 10:39:28 +01:00
parent 11bb12bcee
commit 8157ab760d
Signed by: kageru
GPG Key ID: 8282A2BEA4ADA3D2
4 changed files with 113 additions and 5 deletions

View File

@ -7,7 +7,7 @@ pub fn query_apt(ctx: Context, msg: Message, args: Vec<&str>) {
let query = args.join(" ");
let response: Response = search(
&format!("https://sources.debian.org/api/src/{}/", &query),
EMPTY_RESULT,
|_e| EMPTY_RESULT,
);
if response.versions.len() == 0 {
send(msg.channel_id, "No results", &ctx);

View File

@ -5,6 +5,7 @@ use serde::de::DeserializeOwned;
use serenity::prelude::*;
mod apt;
mod pacman;
mod nix;
extern crate reqwest;
pub struct Handler;
@ -49,6 +50,7 @@ lazy_static! {
let mut command_list = Vec::new();
command_list.push(Command::new("pacman", pacman::query_pacman));
command_list.push(Command::new("apt", apt::query_apt));
command_list.push(Command::new("nix", nix::query_nix));
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 {
return search_inner(url).unwrap_or(fallback);
pub fn search<T: DeserializeOwned>(url: &str, fallback: impl Fn(reqwest::Error) -> T) -> T {
return search_inner(url).unwrap_or_else(fallback);
}
fn search_inner<T: DeserializeOwned>(url: &str) -> Result<T, reqwest::Error> {

106
src/commands/nix.rs Normal file
View File

@ -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))
}

View File

@ -11,7 +11,7 @@ pub fn query_pacman(ctx: Context, msg: Message, args: Vec<&str>) {
"https://www.archlinux.org/packages/search/json/?name={}",
&args[0]
),
EMPTY_RESULT,
|_e| EMPTY_RESULT,
);
// this is 1 for most packages and 2 if there’s a second version in testing
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={}",
&query
),
EMPTY_RESULT,
|_e| EMPTY_RESULT,
);
if response.results.len() == 0 {
send(msg.channel_id, "No results", &ctx);