tapioca-codegen 0.0.1

Type-safe REST client using the OpenAPI Specification
Documentation
use ::inflector::Inflector;
use ::regex::Regex;
use ::syn::Ident;
use ::quote::Tokens;
use ::yaml_rust::Yaml;

use infer::method;
use infer::TokensResult;

fn mod_ident(path: &str) -> Ident {
    let rustified = path.replace('/', " ").trim().to_snake_case();
    let re = Regex::new(r"\{(?P<resource>[^}]+)_\}").unwrap();
    let ident = re.replace_all(rustified.as_str(), "_${resource}_");

    Ident::new(ident)
}

pub(super) fn infer_v3(path: &str, schema: &Yaml) -> TokensResult {
    let path_mod = mod_ident(path);

    let method_schemata = schema.as_hash().expect("Path must be a map.");
    let mut method_impls: Vec<Tokens> = Vec::new();
    let mut path_level_structs = quote!();

    for (method, method_schema) in method_schemata {
        let method = method.as_str().expect("Method must be a string.");
        let (method_impl, maybe_param_structs) = method::infer_v3(&method, &method_schema)?;

        method_impls.push(method_impl);
        if let Some(param_structs) = maybe_param_structs {
            path_level_structs = param_structs;
        }
    }

    Ok(quote! {
        #[allow(non_snake_case)]
        pub mod #path_mod {
            #[allow(unused_imports)]
            use ::tapioca::Body;
            use ::tapioca::Client;
            use ::tapioca::Url;
            use ::tapioca::header;
            use ::tapioca::response::Response;
            #[allow(unused_imports)]
            use ::tapioca::query::QueryString;

            #[allow(unused_imports)]
            use super::auth_scheme;
            #[allow(unused_imports)]
            use super::schema_ref;
            use super::API_URL;
            #[allow(unused_imports)]
            use super::ServerAuth;

            const API_PATH: &'static str = #path;

            #path_level_structs

            #(#method_impls)*
        }
    })
}

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

    #[test]
    fn leading_slash() {
        assert_eq!(mod_ident("/foo"), Ident::new("foo"));
    }

    #[test]
    fn both_slash() {
        assert_eq!(mod_ident("/foo/"), Ident::new("foo"));
    }

    #[test]
    fn no_slash() {
        assert_eq!(mod_ident("foo"), Ident::new("foo"));
    }

    #[test]
    fn trailing_slash() {
        assert_eq!(mod_ident("foo/"), Ident::new("foo"));
    }

    #[test]
    fn multipart() {
        assert_eq!(mod_ident("/foo/bar"), Ident::new("foo_bar"));
    }

    #[test]
    fn resource() {
        assert_eq!(mod_ident("/foo/{id}"), Ident::new("foo__id_"));
    }

    #[test]
    fn multi_resource() {
        assert_eq!(mod_ident("/foo/{id}/{bar}"), Ident::new("foo__id___bar_"));
    }

    #[test]
    fn multipart_resource() {
        assert_eq!(mod_ident("/foo/{id}/bar"), Ident::new("foo__id__bar"));
    }

    #[test]
    fn multipart_multiresource() {
        assert_eq!(mod_ident("/foo/{id}/bar/{bar_id}"), Ident::new("foo__id__bar__bar_id_"));
    }
}