Merge pull request #12 from jrperritt/7

Implement json.Unmarshaler for Flavor
diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md
index 4cc84f6..18f6dc4 100644
--- a/STYLEGUIDE.md
+++ b/STYLEGUIDE.md
@@ -11,7 +11,9 @@
   are not good enough. The link(s) should be to a non-`master` branch. For example,
   a pull request implementing the creation of a Neutron v2 subnet might put the
   following link in the description:
+
   https://github.com/openstack/neutron/blob/stable/mitaka/neutron/api/v2/attributes.py#L749
+
   From that link, a reviewer (or user) can verify the fields in the request/response
   objects in the PR.
 
@@ -37,3 +39,30 @@
   own infrastructure and see an example of usage.
 
 - If in doubt, ask in-line on the PR.
+
+### File Structure
+
+- The following should be used in most cases:
+
+  - `requests.go`: contains all the functions that make HTTP requests and the
+    types associated with the HTTP request (parameters for URL, body, etc)
+  - `results.go`: contains all the response objects and their methods
+  - `urls.go`: contains the endpoints to which the requests are made
+
+### Naming
+
+- For methods on a type in `response.go`, the receiver should be named `r` and the
+  variable into which it will be unmarshalled `s`.
+
+- Functions in `requests.go`, with the exception of functions that return a
+  `pagination.Pager`, should be named returns of the name `r`.
+
+- Functions in `requests.go` that accept request bodies should accept as their
+  last parameter an `interface` named `<Action>OptsBuilder` (eg `CreateOptsBuilder`).
+  This `interface` should have at the least a method named `To<Resource><Action>Map`
+  (eg `ToPortCreateMap`).
+
+- Functions in `requests.go` that accept query strings should accept as their
+  last parameter an `interface` named `<Action>OptsBuilder` (eg `ListOptsBuilder`).
+  This `interface` should have at the least a method named `To<Resource><Action>Query`
+  (eg `ToServerListQuery`).
diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go
index 0edc7d2..a49de0d 100644
--- a/openstack/compute/v2/flavors/results.go
+++ b/openstack/compute/v2/flavors/results.go
@@ -1,15 +1,13 @@
 package flavors
 
 import (
-	"errors"
+	"encoding/json"
+	"strconv"
 
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// ErrCannotInterpret is returned by an Extract call if the response body doesn't have the expected structure.
-var ErrCannotInterpet = errors.New("Unable to interpret a response body.")
-
 // GetResult temporarily holds the response from a Get call.
 type GetResult struct {
 	gophercloud.Result
@@ -29,24 +27,60 @@
 	// The Id field contains the flavor's unique identifier.
 	// For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance.
 	ID string `json:"id"`
-
 	// The Disk and RA< fields provide a measure of storage space offered by the flavor, in GB and MB, respectively.
 	Disk int `json:"disk"`
 	RAM  int `json:"ram"`
-
 	// The Name field provides a human-readable moniker for the flavor.
-	Name string `json:"name"`
-
+	Name       string  `json:"name"`
 	RxTxFactor float64 `json:"rxtx_factor"`
-
 	// Swap indicates how much space is reserved for swap.
 	// If not provided, this field will be set to 0.
 	Swap int `json:"swap"`
-
 	// VCPUs indicates how many (virtual) CPUs are available for this flavor.
 	VCPUs int `json:"vcpus"`
 }
 
+func (f *Flavor) UnmarshalJSON(b []byte) error {
+	var flavor struct {
+		ID         string      `json:"id"`
+		Disk       int         `json:"disk"`
+		RAM        int         `json:"ram"`
+		Name       string      `json:"name"`
+		RxTxFactor float64     `json:"rxtx_factor"`
+		Swap       interface{} `json:"swap"`
+		VCPUs      int         `json:"vcpus"`
+	}
+	err := json.Unmarshal(b, &flavor)
+	if err != nil {
+		return err
+	}
+
+	f.ID = flavor.ID
+	f.Disk = flavor.Disk
+	f.RAM = flavor.RAM
+	f.Name = flavor.Name
+	f.RxTxFactor = flavor.RxTxFactor
+	f.VCPUs = flavor.VCPUs
+
+	switch t := flavor.Swap.(type) {
+	case float64:
+		f.Swap = int(t)
+	case string:
+		switch t {
+		case "":
+			f.Swap = 0
+		default:
+			swap, err := strconv.ParseFloat(t, 64)
+			if err != nil {
+				return err
+			}
+			f.Swap = int(swap)
+		}
+	}
+
+	return nil
+}
+
 // FlavorPage contains a single page of the response from a List call.
 type FlavorPage struct {
 	pagination.LinkedPageBase
diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go
index e86512a..1b96933 100644
--- a/openstack/compute/v2/flavors/testing/requests_test.go
+++ b/openstack/compute/v2/flavors/testing/requests_test.go
@@ -35,14 +35,16 @@
 								"name": "m1.tiny",
 								"disk": 1,
 								"ram": 512,
-								"vcpus": 1
+								"vcpus": 1,
+								"swap":""
 							},
 							{
 								"id": "2",
 								"name": "m2.small",
 								"disk": 10,
 								"ram": 1024,
-								"vcpus": 2
+								"vcpus": 2,
+								"swap": 1000
 							}
 						],
 						"flavors_links": [
@@ -70,8 +72,8 @@
 		}
 
 		expected := []flavors.Flavor{
-			{ID: "1", Name: "m1.tiny", Disk: 1, RAM: 512, VCPUs: 1},
-			{ID: "2", Name: "m2.small", Disk: 10, RAM: 1024, VCPUs: 2},
+			{ID: "1", Name: "m1.tiny", Disk: 1, RAM: 512, VCPUs: 1, Swap: 0},
+			{ID: "2", Name: "m2.small", Disk: 10, RAM: 1024, VCPUs: 2, Swap: 1000},
 		}
 
 		if !reflect.DeepEqual(expected, actual) {
@@ -105,7 +107,8 @@
 					"disk": 1,
 					"ram": 512,
 					"vcpus": 1,
-					"rxtx_factor": 1
+					"rxtx_factor": 1,
+					"swap": ""
 				}
 			}
 		`)
@@ -123,6 +126,7 @@
 		RAM:        512,
 		VCPUs:      1,
 		RxTxFactor: 1,
+		Swap:       0,
 	}
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("Expected %#v, but was %#v", expected, actual)