Skip to content

Commit

Permalink
fix broken import command (#1, #19)
Browse files Browse the repository at this point in the history
  • Loading branch information
climech committed Apr 5, 2021
1 parent 86678fa commit 28a79d8
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 24 deletions.
47 changes: 37 additions & 10 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,29 +95,56 @@ func (a *App) AddChild(name string, parent interface{}) (*multitree.Node, error)
return a.Database.GetGraph(nodeID)
}

func (a *App) AddTree(node *multitree.Node, parent interface{}) (int64, error) {
for _, n := range node.All() {
func validateTree(root *multitree.Node) error {
for _, n := range root.All() {
if err := multitree.ValidateNodeName(n.Name); err != nil {
return 0, NewError(ErrInvalidName, err.Error())
return NewError(ErrInvalidName, err.Error())
}
if err := multitree.ValidateDateNodeName(n.Name); err == nil {
return 0, NewError(ErrInvalidName,
return NewError(ErrInvalidName,
fmt.Sprintf("%v is a reserved name", n.Name))
}
}
return nil
}

// AddRootTree creates a new tree at the root level. It returns the root ID.
func (a *App) AddRootTree(tree *multitree.Node) (int64, error) {
if err := validateTree(tree); err != nil {
return 0, err
}
return a.Database.CreateTree(tree, 0)
}

// AddChildTree creates a new tree and links parent to its root. It returns the
// root ID.
func (a *App) AddChildTree(tree *multitree.Node, parent interface{}) (int64, error) {
if err := validateTree(tree); err != nil {
return 0, err
}

parentID, err := a.selectorToID(parent)
if err != nil {
return 0, NewError(ErrInvalidSelector, err.Error())
}
id, err := a.Database.CreateTree(node, parentID)
if err != nil {
e, ok := err.(sqlite.Error)
if ok && e.ExtendedCode == sqlite.ErrConstraintForeignKey {

var rootID int64
var createErr error
if parentID == 0 {
rootID, createErr = a.Database.CreateTreeAsChildOfDateNode(parent.(string), tree)
} else {
rootID, createErr = a.Database.CreateTree(tree, parentID)
}

if createErr != nil {
if e, ok := createErr.(sqlite.Error); ok && e.ExtendedCode == sqlite.ErrConstraintForeignKey {
return 0, NewError(ErrNotFound, "parent does not exist")
} else {
return 0, createErr
}
return 0, err
}
return id, nil

return rootID, nil
}

func (a *App) RenameNode(selector interface{}, name string) error {
Expand Down
6 changes: 4 additions & 2 deletions cmd/grit/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,13 +369,15 @@ func cmdRemove(cmd *cli.Cmd) {
func cmdImport(cmd *cli.Cmd) {
cmd.Spec = "[ -p=<predecessor> | -r ] [FILENAME]"
today := time.Now().Format("2006-01-02")

var (
filename = cmd.StringArg("FILENAME", "",
"file containing tab-indented lines")
predecessor = cmd.StringOpt("p predecessor", today,
"predecessor for the tree root(s)")
makeRoot = cmd.BoolOpt("r root", false, "create top-level tree(s)")
)

cmd.Action = func() {
a, err := app.New()
if err != nil {
Expand Down Expand Up @@ -407,9 +409,9 @@ func cmdImport(cmd *cli.Cmd) {
var id int64
var err error
if *makeRoot {
id, err = a.AddTree(root, 0)
id, err = a.AddRootTree(root)
} else {
id, err = a.AddTree(root, *predecessor)
id, err = a.AddChildTree(root, *predecessor)
}
if err != nil {
e := fmt.Errorf("Couldn't import tree: %v", err)
Expand Down
14 changes: 13 additions & 1 deletion db/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func createTree(tx *sql.Tx, node *multitree.Node, parentID int64) (int64, error)
var retErr error

tree.TraverseDescendants(func(current *multitree.Node, stop func()) {
var pid int64
pid := parentID
parents := current.Parents()
if len(parents) > 0 {
pid = parents[0].ID
Expand All @@ -228,6 +228,18 @@ func createTree(tx *sql.Tx, node *multitree.Node, parentID int64) (int64, error)
if retErr != nil {
return 0, retErr
}

// Update ancestors, if any.
if parentID != 0 {
g, err := getGraph(tx, tree.ID)
if err != nil {
return 0, err
}
if err := backpropCompletion(tx, g); err != nil {
return 0, err
}
}

return tree.ID, nil
}

Expand Down
30 changes: 19 additions & 11 deletions multitree/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,15 @@ func ImportNodes(reader io.Reader) ([]*Node, error) {
lineNum := 1

for scanner.Scan() {
indent, name, err := parseImportLine(scanner.Text())
if err != nil {
indent, name := parseImportLine(scanner.Text())

// Ignore empty lines.
if name == "" {
lineNum++
continue
}

if err := ValidateNodeName(name); err != nil {
return nil, fmt.Errorf("line %d: %v", lineNum, err)
}

Expand All @@ -37,28 +44,29 @@ func ImportNodes(reader io.Reader) ([]*Node, error) {
var newNode *Node
if len(stack) == 0 {
newNode = NewNode(name)
newNode.ID = 1
roots = append(roots, newNode)
} else {
topNode := stack[len(stack)-1].node
newNode = topNode.New(name)
_ = LinkNodes(topNode, newNode)
}

stack = append(stack, &stackItem{indent: indent, node: newNode})
lineNum++
}

return roots, nil
}

func parseImportLine(line string) (int, string, error) {
var indent, i int
for i < len(line) && line[i] == '\t' {
indent++
i++
// parseImportLine returns the node's indent level and name.
func parseImportLine(line string) (int, string) {
if len(line) == 0 {
return 0, ""
}
name := line[indent:]
if err := ValidateNodeName(name); err != nil {
return 0, "", err
var indent int
for i := 0; i < len(line) && (line[i] == '\t' || line[i] == ' '); i++ {
indent++
}
return indent, name, nil
return indent, line[indent:]
}

0 comments on commit 28a79d8

Please sign in to comment.