paths/patterns.go

Summary

Maintainability
A
0 mins
Test Coverage
package paths

import (
    "fmt"
    "os"
    "path/filepath"
    "strings"

    "github.com/efritz/ij/logging"
    "github.com/mattn/go-zglob"
)

type FilePair struct {
    Src  string
    Dest string
}

func runOnPatterns(
    patterns []string,
    root string,
    logger logging.Logger,
    target func(FilePair) error,
) error {
    for _, pattern := range patterns {
        if err := runOnPattern(pattern, root, logger, target); err != nil {
            return err
        }
    }

    return nil
}

func runOnPattern(
    pattern string,
    root string,
    logger logging.Logger,
    target func(FilePair) error,
) error {
    if strings.Contains(pattern, ":") {
        if strings.Contains(pattern, "*") {
            return fmt.Errorf(
                "illegal pattern %s (wildcards and explicit destinations are mutually exclusive)",
                pattern,
            )
        }

        return runOnSplitPattern(pattern, root, logger, target)
    }

    return runOnGlobPattern(pattern, root, logger, target)
}

func sanitize(path, root string) (string, error) {
    absPath, err := filepath.Abs(path)
    if err != nil {
        return "", fmt.Errorf(
            "failed to normalize path '%s': %s",
            path,
            err.Error(),
        )
    }

    if !strings.HasPrefix(absPath, root) {
        return "", fmt.Errorf(
            "path '%s' is outside of workspace directory",
            absPath,
        )
    }

    return absPath, nil
}

//
// Helpers

func constructBlacklist(root string, patterns []string) (map[string]struct{}, error) {
    var (
        blacklist   = map[string]struct{}{}
        allPatterns = append(DefaultBlacklist, patterns...)
    )

    for _, pattern := range allPatterns {
        if strings.Contains(pattern, ":") {
            return nil, fmt.Errorf("blacklist contains destination path: %s", pattern)
        }
    }

    err := runOnPatterns(allPatterns, root, logging.NilLogger, func(pair FilePair) error {
        blacklist[pair.Src] = struct{}{}
        return nil
    })

    if err != nil {
        return nil, err
    }

    return blacklist, nil
}

func runOnSplitPattern(
    pattern string,
    root string,
    logger logging.Logger,
    target func(FilePair) error,
) error {
    src, dest := splitPath(pattern)

    if exists, err := PathExists(filepath.Join(root, src)); err != nil || !exists {
        logger.Warn(
            nil,
            "no files matched the pattern '%s'",
            src,
        )

        return err
    }

    return target(FilePair{
        Src:  filepath.Join(root, src),
        Dest: filepath.Join(root, dest),
    })
}

func runOnGlobPattern(
    pattern string,
    root string,
    logger logging.Logger,
    target func(FilePair) error,
) error {
    paths, err := zglob.Glob(filepath.Join(root, pattern))
    if err != nil {
        if err == os.ErrNotExist {
            logger.Warn(
                nil,
                "no files matched the pattern '%s'",
                pattern,
            )

            return nil
        }

        return err
    }

    if len(paths) == 0 {
        logger.Warn(
            nil,
            "no files matched the pattern '%s'",
            pattern,
        )
    }

    for _, path := range paths {
        if err := target(FilePair{path, path}); err != nil {
            return err
        }
    }

    return nil
}

func splitPath(path string) (string, string) {
    if parts := strings.SplitN(path, ":", 2); len(parts) == 2 {
        return parts[0], parts[1]
    }

    return path, path
}