serde-rs / serde

Serialization framework for Rust
https://serde.rs/
Apache License 2.0
8.96k stars 756 forks source link

Question on Obtaining Values from Maps with Deserialize #394

Closed mmstick closed 8 years ago

mmstick commented 8 years ago

I'm having a predicament where the response that I am getting from a website is a list of anonymous values contained within a map, so I created a struct that contains the values of each element, and then created a struct to contain a vector of those elements. It seems that I'm not able to do this though because it then errors that the Result return value in pub fn check_account(account: &str) -> Result<Status, Error> has the wrong number of type arguments. It expects 0, but finds 2, and there should be 2, which is confusing. What's the method of obtaining values from maps?

Source Code

use std::io::Read;
use hyper::Client;
use hyper::header::{Connection, UserAgent};
use hyper::status::StatusCode;
use serde_json::de as JsonDecoder;

const URL: &'static str = "https://haveibeenpwned.com/api";

pub enum Status { Ok, Pwned(Results) }

pub enum Error { Request(String), Decode(String, String) }

#[derive(Deserialize)]
pub struct Results {
    result: Vec<Result>,
}

#[derive(Deserialize)]
pub struct Result {
    #[serde(rename="Name")]
    pub name: String,
    #[serde(rename="Title")]
    pub title: String,
    #[serde(rename="Domain")]
    pub domain: String,
    #[serde(rename="BreachDate")]
    pub breach_date: String,
    #[serde(rename="AddedDate")]
    pub added_date: String,
    #[serde(rename="PwnCount")]
    pub pwn_count: i32,
    #[serde(rename="Description")]
    pub description: String,
    #[serde(rename="DataClasses")]
    pub data_classes: Vec<String>,
    #[serde(rename="IsSensitive")]
    pub sensitive: bool,
    #[serde(rename="IsRetired")]
    pub retired: bool,
}

pub fn check_account(account: &str) -> Result<Status, Error> {
    let url = format!("{}/{}/{}/{}", URL, "v2", "breachedaccount", account);
    let client = Client::new();
    let request = client.get(&url).header(Connection::close()).header(UserAgent(String::from("hyper")));
    let mut response = match request.send() {
        Ok(response) => response,
        Err(error)   => return Err(Error::Request(error.to_string()))
    };

    if response.status == StatusCode::Ok {
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        match JsonDecoder::from_slice::<Results>(body.as_bytes()) {
            Ok(results) => Ok(Status::Pwned(results)),
            Err(error)  => Err(Error::Decode(error.to_string(), body)),
        }
    } else {
        Ok(Status::Ok)
    }
}
dtolnay commented 8 years ago

You need to call your struct something other than Result because Result is this built-in type.

If you absolutely do not want to rename your struct, you need to change the signature to:

pub fn check_account(account: &str) -> ::std::result::Result<Status, Error> {

or:

use std::result::Result as StdResult;
pub fn check_account(account: &str) -> StdResult<Status, Error> {
mmstick commented 8 years ago

Well that was simple, although the setup seems to give this error:

Checking mmstick
rustcyp: unable to decode response: invalid type: Map at line 1 column 2
[{"Title":"Battlefield Heroes","Name":"BattlefieldHeroes","Domain":"battlefieldheroes.com","BreachDate":"2011-06-26","AddedDate":"2014-01-23T13:10:29Z","PwnCount":530270,"Description":"In June 2011 as part of a final breached data dump, the hacker collective &quot;LulzSec&quot; <a href=\"http://www.rockpapershotgun.com/2011/06/26/lulzsec-over-release-battlefield-heroes-data\" target=\"_blank\">obtained and released over half a million usernames and passwords from the game Battlefield Heroes</a>. The passwords were stored as MD5 hashes with no salt and many were easily converted back to their plain text versions.","DataClasses":["Passwords","Usernames"],"IsVerified":true,"IsSensitive":false,"IsActive":true,"IsRetired":false,"LogoType":"svg"}]
dtolnay commented 8 years ago

It works if you do type Results = Vec<Result> instead of your struct. There is no "result" key in the response from haveibeenpwned.com.