usps-api 0.0.1

A wrapper around the USPS WebTools API
Documentation
//! # USPS WebTools
//! A Rust library for interfacing with the USPS Web API 
#![deny(missing_docs,
        missing_debug_implementations, missing_copy_implementations,
        trivial_casts, trivial_numeric_casts,
        unstable_features, unsafe_code,
        unused_import_braces, unused_qualifications)]

/* Standard Libary */
use std::env;

/* Third Party Libraries */
use reqwest;
use roxmltree;

/* Modules */
pub mod address;

/* Static Strings */
const SECURE_ENDPOINT:   &'static str = "https://secure.shippingapis.com/ShippingAPI.dll?API=";
const UNSECURE_ENDPOINT: &'static str = "http://production.shippingapis.com/ShippingAPI.dll?API=";

const ENV_USER_ID: &'static str = "USPS_USER_ID";
const ENV_PASSWORD: &'static str = "USPS_PASSWORD";

/// This struct is how you will make API calls to the USPS. It can be initialized with a hardcoded user_id and password using the 'init' contructor or it can attempt to capture these values from the environment (USPS_USER_ID and USPS_PASSWORD respectively) using the 'new' constructor.
#[derive(Debug, PartialEq)]
pub struct USPSWebTool {
    secure: bool,
    user_id: String,
    password: String,
}

/* Constructor and Setter Implementations */
impl USPSWebTool {
    /// Used to build a new USPSWebTool struct by specifying the USPS authentication credentials directly."
	/// # Example
	/// ```
	/// # use usps_api::USPSWebTool;
    /// let usps_api = USPSWebTool::init("XXXX", "YYYY");
	/// ```
    pub fn init(user_id: &str, password: &str) -> Self {
        USPSWebTool {
            secure: true,
            user_id: String::from(user_id),
            password: String::from(password)
        }
    }

    /// Used to build a new USPSWebTool struct by looking at the environment variables "USPS_USER_ID" and "USPS_PASSWORD to specify the USPS authentication credentials." 
	/// # Example
	/// ```
	/// # use usps_api::USPSWebTool;
    /// # use std::env;
    /// # fn main() -> Result<(), env::VarError> {
    /// env::set_var("USPS_USER_ID", "XXXX");
    /// env::set_var("USPS_PASSWORD", "YYYY");
    /// let usps_api = USPSWebTool::new()?;
    /// # Ok(())
    /// # }
	/// ```
    pub fn new() -> Result<Self, env::VarError> {
        let user_id = env::var(ENV_USER_ID)?;
        let password = env::var(ENV_PASSWORD)?;

        Ok(USPSWebTool {
            secure: true,
            user_id,
            password,
        })
    }

    /* Disables security */
    /// Will use the unsecured endpoint for communication with the USPS API. This feature is **not** recommended.
    ///
	/// # Example
	/// ```
	/// # use usps_api::USPSWebTool;
    /// let usps_api = USPSWebTool::init("XXXX", "YYYY").use_http();
	/// ```
    pub fn use_http(mut self) -> Self {
       self.secure = false;
       self
    }

    /// Accepts a USPS Address and returns a result with the 'correct' form of the address.
    pub fn verify_address(&self, address: address::USPSAddress) -> Result<address::USPSAddress, Box<dyn std::error::Error>> {
        let mut req;
        if self.secure {
            req = String::from(SECURE_ENDPOINT);
        } else {
            req = String::from(UNSECURE_ENDPOINT);
        }
        
        /* Construct the query URL */
        req.push_str("Verify&XML=");
        req.push_str(&format!("<AddressValidateRequest USERID=\"{}\"><Revision>1</Revision>", self.user_id));
        req.push_str(&address.xml());
        req.push_str("</AddressValidateRequest>"); 
        
        /* Fetch the result */
        let body = reqwest::get(&req)?.text()?;
        println!("{:?}", body);

        /* Check if the XML is an error */
        let xml = roxmltree::Document::parse(&body)?;
        /* if xml.root_element().tag_name().name() == "Error" { */ 
        /* } */
        
        Ok(address::USPSAddress::from_xml(xml))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const DUMMY_UN: &'static str = "USERNAME";
    const DUMMY_PW: &'static str = "PASSWORD";

    #[test]
    fn init_constructor() {
        let dummy_struct = USPSWebTool {
            secure: true,
            user_id: String::from(DUMMY_UN), 
            password: String::from(DUMMY_PW),
        };

        assert_eq!(USPSWebTool::init(DUMMY_UN, DUMMY_PW), dummy_struct);
    }

    #[test]
    fn new_constructor() {
        env::set_var(ENV_USER_ID, DUMMY_UN);
        env::set_var(ENV_PASSWORD, DUMMY_PW);

        let dummy_struct = USPSWebTool {
            secure: true,
            user_id: String::from(DUMMY_UN), 
            password: String::from(DUMMY_PW),
        };

        assert_eq!(USPSWebTool::new().unwrap(), dummy_struct);
    }

    #[test]
    #[should_panic]
    fn new_contructor_no_env() {
        env::remove_var(ENV_USER_ID);
        env::remove_var(ENV_PASSWORD);

        let dummy_struct = USPSWebTool {
            secure: true,
            user_id: String::from(DUMMY_UN), 
            password: String::from(DUMMY_PW),
        };

        assert_eq!(USPSWebTool::new().unwrap(), dummy_struct);
    }

    #[test]
    fn use_http() {
        let dummy_struct = USPSWebTool {
            secure: false,
            user_id: String::from(DUMMY_UN), 
            password: String::from(DUMMY_PW),
        };

        assert_eq!(USPSWebTool::init(DUMMY_UN, DUMMY_PW).use_http(), 
                   dummy_struct);
    }

    mod address_test {
        use super::*;

        #[test]
        fn test_address_1() {

            let api = USPSWebTool::init("348XVRQT1293", "088NI00MI876");
            let home = address::USPSAddress::quick("429 2nd Ave W", "104", "Seattle", "WA", "98119");
            api.verify_address(home).unwrap();
            

        }
    }
}