Skip to content

Commit

Permalink
Merge pull request #515 from mutagen-io/v018-development
Browse files Browse the repository at this point in the history
api: ensure all enumeration types implement UnmarshalText
  • Loading branch information
xenoscopic authored Oct 18, 2024
2 parents 15e9d73 + fe518d1 commit e8911b2
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 2 deletions.
23 changes: 23 additions & 0 deletions pkg/forwarding/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ func (s Status) MarshalText() ([]byte, error) {
return []byte(result), nil
}

// UnmarshalText implements encoding.TextUnmarshaler.UnmarshalText.
func (s *Status) UnmarshalText(textBytes []byte) error {
// Convert the bytes to a string.
text := string(textBytes)

// Convert to a forwarding status.
switch text {
case "disconnected":
*s = Status_Disconnected
case "connecting-source":
*s = Status_ConnectingSource
case "connecting-destination":
*s = Status_ConnectingDestination
case "forwarding":
*s = Status_ForwardingConnections
default:
return fmt.Errorf("unknown forwarding status: %s", text)
}

// Success.
return nil
}

// ensureValid ensures that EndpointState's invariants are respected.
func (s *EndpointState) ensureValid() error {
// A nil endpoint state is not valid.
Expand Down
40 changes: 39 additions & 1 deletion pkg/forwarding/state_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,41 @@
package forwarding

// TODO: Implement.
import (
"testing"
)

// TestStatusUnmarshal tests that unmarshaling from a string specification
// succeeeds for Status.
func TestStatusUnmarshal(t *testing.T) {
// Set up test cases.
testCases := []struct {
text string
expected Status
expectFailure bool
}{
{"", Status_Disconnected, true},
{"asdf", Status_Disconnected, true},
{"disconnected", Status_Disconnected, false},
{"connecting-source", Status_ConnectingSource, false},
{"connecting-destination", Status_ConnectingDestination, false},
{"forwarding", Status_ForwardingConnections, false},
}

// Process test cases.
for _, testCase := range testCases {
var status Status
if err := status.UnmarshalText([]byte(testCase.text)); err != nil {
if !testCase.expectFailure {
t.Errorf("unable to unmarshal text (%s): %s", testCase.text, err)
}
} else if testCase.expectFailure {
t.Error("unmarshaling succeeded unexpectedly for text:", testCase.text)
} else if status != testCase.expected {
t.Errorf(
"unmarshaled status (%s) does not match expected (%s)",
status,
testCase.expected,
)
}
}
}
28 changes: 28 additions & 0 deletions pkg/synchronization/core/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package core
import (
"bytes"
"errors"
"fmt"
"strings"

"github.com/mutagen-io/mutagen/pkg/synchronization/core/fastpath"
Expand Down Expand Up @@ -38,6 +39,33 @@ func (k EntryKind) MarshalText() ([]byte, error) {
return []byte(result), nil
}

// UnmarshalText implements encoding.TextUnmarshaler.UnmarshalText.
func (k *EntryKind) UnmarshalText(textBytes []byte) error {
// Convert the bytes to a string.
text := string(textBytes)

// Convert to a forwarding status.
switch text {
case "directory":
*k = EntryKind_Directory
case "file":
*k = EntryKind_File
case "symlink":
*k = EntryKind_SymbolicLink
case "untracked":
*k = EntryKind_Untracked
case "problematic":
*k = EntryKind_Problematic
case "phantom-directory":
*k = EntryKind_PhantomDirectory
default:
return fmt.Errorf("unknown entry kind: %s", text)
}

// Success.
return nil
}

// EnsureValid ensures that Entry's invariants are respected. If synchronizable
// is true, then unsynchronizable content will be considered invalid.
func (e *Entry) EnsureValid(synchronizable bool) error {
Expand Down
38 changes: 38 additions & 0 deletions pkg/synchronization/core/entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,44 @@ func TestEntryKindSynchronizable(t *testing.T) {
}
}

// TestEntryKindUnmarshal tests that unmarshaling from a string specification
// succeeeds for EntryKind.
func TestEntryKindUnmarshal(t *testing.T) {
// Set up test cases.
testCases := []struct {
text string
expected EntryKind
expectFailure bool
}{
{"", EntryKind_Directory, true},
{"asdf", EntryKind_Directory, true},
{"directory", EntryKind_Directory, false},
{"file", EntryKind_File, false},
{"symlink", EntryKind_SymbolicLink, false},
{"untracked", EntryKind_Untracked, false},
{"problematic", EntryKind_Problematic, false},
{"phantom-directory", EntryKind_PhantomDirectory, false},
}

// Process test cases.
for _, testCase := range testCases {
var kind EntryKind
if err := kind.UnmarshalText([]byte(testCase.text)); err != nil {
if !testCase.expectFailure {
t.Errorf("unable to unmarshal text (%s): %s", testCase.text, err)
}
} else if testCase.expectFailure {
t.Error("unmarshaling succeeded unexpectedly for text:", testCase.text)
} else if kind != testCase.expected {
t.Errorf(
"unmarshaled entry kind (%s) does not match expected (%s)",
kind,
testCase.expected,
)
}
}
}

func init() {
// Enable wildcard problem matching for tests.
entryEqualWildcardProblemMatch = true
Expand Down
43 changes: 43 additions & 0 deletions pkg/synchronization/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,49 @@ func (s Status) MarshalText() ([]byte, error) {
return []byte(result), nil
}

// UnmarshalText implements encoding.TextUnmarshaler.UnmarshalText.
func (s *Status) UnmarshalText(textBytes []byte) error {
// Convert the bytes to a string.
text := string(textBytes)

// Convert to a synchronization status.
switch text {
case "disconnected":
*s = Status_Disconnected
case "halted-on-root-emptied":
*s = Status_HaltedOnRootEmptied
case "halted-on-root-deletion":
*s = Status_HaltedOnRootDeletion
case "halted-on-root-type-change":
*s = Status_HaltedOnRootTypeChange
case "connecting-alpha":
*s = Status_ConnectingAlpha
case "connecting-beta":
*s = Status_ConnectingBeta
case "watching":
*s = Status_Watching
case "scanning":
*s = Status_Scanning
case "waiting-for-rescan":
*s = Status_WaitingForRescan
case "reconciling":
*s = Status_Reconciling
case "staging-alpha":
*s = Status_StagingAlpha
case "staging-beta":
*s = Status_StagingBeta
case "transitioning":
*s = Status_Transitioning
case "saving":
*s = Status_Saving
default:
return fmt.Errorf("unknown synchronization status: %s", text)
}

// Success.
return nil
}

// ensureValid ensures that EndpointState's invariants are respected.
func (s *EndpointState) ensureValid() error {
// A nil endpoint state is not valid.
Expand Down
50 changes: 49 additions & 1 deletion pkg/synchronization/state_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,51 @@
package synchronization

// TODO: Implement.
import (
"testing"
)

// TestStatusUnmarshal tests that unmarshaling from a string specification
// succeeeds for Status.
func TestStatusUnmarshal(t *testing.T) {
// Set up test cases.
testCases := []struct {
text string
expected Status
expectFailure bool
}{
{"", Status_Disconnected, true},
{"asdf", Status_Disconnected, true},
{"disconnected", Status_Disconnected, false},
{"halted-on-root-emptied", Status_HaltedOnRootEmptied, false},
{"halted-on-root-deletion", Status_HaltedOnRootDeletion, false},
{"halted-on-root-type-change", Status_HaltedOnRootTypeChange, false},
{"connecting-alpha", Status_ConnectingAlpha, false},
{"connecting-beta", Status_ConnectingBeta, false},
{"watching", Status_Watching, false},
{"scanning", Status_Scanning, false},
{"waiting-for-rescan", Status_WaitingForRescan, false},
{"reconciling", Status_Reconciling, false},
{"staging-alpha", Status_StagingAlpha, false},
{"staging-beta", Status_StagingBeta, false},
{"transitioning", Status_Transitioning, false},
{"saving", Status_Saving, false},
}

// Process test cases.
for _, testCase := range testCases {
var status Status
if err := status.UnmarshalText([]byte(testCase.text)); err != nil {
if !testCase.expectFailure {
t.Errorf("unable to unmarshal text (%s): %s", testCase.text, err)
}
} else if testCase.expectFailure {
t.Error("unmarshaling succeeded unexpectedly for text:", testCase.text)
} else if status != testCase.expected {
t.Errorf(
"unmarshaled status (%s) does not match expected (%s)",
status,
testCase.expected,
)
}
}
}

0 comments on commit e8911b2

Please sign in to comment.