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 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);
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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={}",
|
||||
&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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user