2019-10-31 10:39:28 +01:00
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 ) {
2019-10-31 11:45:16 +01:00
Some ( result ) = > respond_with_results ( msg . channel_id , & [ result ] , & ctx ) ,
2019-10-31 10:39:28 +01:00
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 ) )
}