datastore: allow field names ending in '.' (#52)

restructures Load logic to map fields ending in '.' to the anonymous
struct field specified in the codec

Fixes #41
diff --git a/datastore/datastore_test.go b/datastore/datastore_test.go
index 8a297df..b3888e9 100644
--- a/datastore/datastore_test.go
+++ b/datastore/datastore_test.go
@@ -71,6 +71,8 @@
 	testGeoPt0   = appengine.GeoPoint{Lat: 1.2, Lng: 3.4}
 	testGeoPt1   = appengine.GeoPoint{Lat: 5, Lng: 10}
 	testBadGeoPt = appengine.GeoPoint{Lat: 1000, Lng: 34}
+
+	now = time.Unix(1e9, 0).UTC()
 )
 
 type B0 struct {
@@ -351,6 +353,14 @@
 	return LoadStruct(d, props)
 }
 
+type EmbeddedTime struct {
+	time.Time
+}
+
+type SpecialTime struct {
+	MyTime EmbeddedTime
+}
+
 func (d *Doubler) Save() ([]Property, error) {
 	// Save the default Property slice to an in-memory buffer (a PropertyList).
 	props, err := SaveStruct(d)
@@ -1413,6 +1423,22 @@
 		"",
 		"",
 	},
+	{
+		"embedded time field",
+		&SpecialTime{MyTime: EmbeddedTime{now}},
+		&SpecialTime{MyTime: EmbeddedTime{now}},
+		"",
+		"",
+	},
+	{
+		"embedded time load",
+		&PropertyList{
+			Property{Name: "MyTime.", Value: now, NoIndex: false, Multiple: false},
+		},
+		&SpecialTime{MyTime: EmbeddedTime{now}},
+		"",
+		"",
+	},
 }
 
 // checkErr returns the empty string if either both want and err are zero,
diff --git a/datastore/load.go b/datastore/load.go
index 7878cbf..38a6365 100644
--- a/datastore/load.go
+++ b/datastore/load.go
@@ -65,36 +65,35 @@
 	var sliceIndex int
 
 	name := p.Name
-	for name != "" {
-		// First we try to find a field with name matching
-		// the value of 'name' exactly.
-		decoder, ok := codec.fields[name]
-		if ok {
-			name = ""
-		} else {
-			// Now try for legacy flattened nested field (named eg. "A.B.C.D").
 
-			parent := name
-			child := ""
+	// If name ends with a '.', the last field is anonymous.
+	// In this case, strings.Split will give us "" as the
+	// last element of our fields slice, which will match the ""
+	// field name in the substruct codec.
+	fields := strings.Split(name, ".")
 
-			// Cut off the last field (delimited by ".") and find its parent
-			// in the codec.
-			// eg. for name "A.B.C.D", split off "A.B.C" and try to
-			// find a field in the codec with this name.
-			// Loop again with "A.B", etc.
-			for !ok {
-				i := strings.LastIndex(parent, ".")
-				if i < 0 {
-					return "no such struct field"
-				}
-				if i == len(name)-1 {
-					return "field name cannot end with '.'"
-				}
-				parent, child = name[:i], name[i+1:]
-				decoder, ok = codec.fields[parent]
+	for len(fields) > 0 {
+		var decoder fieldCodec
+		var ok bool
+
+		// Cut off the last field (delimited by ".") and find its parent
+		// in the codec.
+		// eg. for name "A.B.C.D", split off "A.B.C" and try to
+		// find a field in the codec with this name.
+		// Loop again with "A.B", etc.
+		for i := len(fields); i > 0; i-- {
+			parent := strings.Join(fields[:i], ".")
+			decoder, ok = codec.fields[parent]
+			if ok {
+				fields = fields[i:]
+				break
 			}
+		}
 
-			name = child
+		// If we never found a matching field in the codec, return
+		// error message.
+		if !ok {
+			return "no such struct field"
 		}
 
 		v = initField(structValue, decoder.path)