Skip to content

Instantly share code, notes, and snippets.

@blakemerryman
Forked from listrophy/Glob.swift
Last active January 31, 2019 22:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save blakemerryman/76312e1cbf8aec248167 to your computer and use it in GitHub Desktop.
Save blakemerryman/76312e1cbf8aec248167 to your computer and use it in GitHub Desktop.
Wrap glob(3) in Swift
//
// Glob.swift
//
// Created by Brad Grzesiak on 6/25/15.
// Copyright © 2015 Bendyworks Inc.
// Released under the Apache v2 License.
//
import Darwin
class Glob: CollectionType {
var paths = [String]()
var startIndex: Int { return paths.startIndex }
var endIndex: Int { return paths.endIndex }
init(pattern: String) {
var gt = glob_t()
let cPattern = strdup(pattern)
if executeGlob(cPattern, gt: &gt) {
populateFiles(gt)
}
globfree(&gt)
}
// MARK: Private
private var globalFlags = GLOB_TILDE | GLOB_BRACE | GLOB_MARK
private func executeGlob(pattern: UnsafePointer<CChar>, gt: UnsafeMutablePointer<glob_t>) -> Bool {
return 0 == glob(pattern, globalFlags, nil, gt)
}
private func cPattern(pattern: String) -> UnsafeMutablePointer<Int8>? {
return strdup(pattern)
}
private func populateFiles(gt: glob_t) {
for var i = 0; i < Int(gt.gl_matchc); i++ {
if let path = String.fromCString(gt.gl_pathv[i]) {
paths.append(path)
}
}
}
// MARK: Subscript Support
subscript(i: Int) -> String {
return paths[i]
}
}
//
// GlobTester.swift
//
// Created by Brad Grzesiak on 6/25/15.
// Copyright © 2015 Bendyworks Inc.
// Released under the Apache v2 License.
//
import XCTest
class GlobTester: XCTestCase {
let tmpFiles = ["foo", "bar", "baz"]
var tmpDir: String?
override func setUp() {
super.setUp()
var tmpDirTmpl = "/tmp/glob-test.XXXXX".cStringUsingEncoding(NSUTF8StringEncoding)!
self.tmpDir = String.fromCString(mkdtemp(&tmpDirTmpl))
for file in tmpFiles {
close(open("\(tmpDir!)/\(file)", O_CREAT))
}
}
override func tearDown() {
for file in tmpFiles {
unlink("\(tmpDir!)/\(file)")
}
rmdir(self.tmpDir!)
super.tearDown()
}
func testBraces() {
let pattern = "\(tmpDir!)/ba{r,y,z}"
let glob = Glob(pattern: pattern)
var contents = [String]()
for file in glob {
contents.append(file)
}
XCTAssertEqual(contents, ["\(tmpDir!)/bar", "\(tmpDir!)/baz"], "matching with braces failed")
}
func testNothingMatches() {
let pattern = "\(tmpDir!)/nothing"
let glob = Glob(pattern: pattern)
var contents = [String]()
for file in glob {
contents.append(file)
}
XCTAssertEqual(contents, [], "expected empty list of files")
}
func testDirectAccess() {
let pattern = "\(tmpDir!)/ba{r,y,z}"
let glob = Glob(pattern: pattern)
XCTAssertEqual(glob.paths, ["\(tmpDir!)/bar", "\(tmpDir!)/baz"], "matching with braces failed")
}
func testIterateTwice() {
let pattern = "\(tmpDir!)/ba{r,y,z}"
let glob = Glob(pattern: pattern)
var contents1 = [String]()
var contents2 = [String]()
for file in glob {
contents1.append(file)
}
let filesAfterOnce = glob.paths
for file in glob {
contents2.append(file)
}
XCTAssertEqual(contents1, contents2, "results for calling for-in twice are the same")
XCTAssertEqual(glob.paths, filesAfterOnce, "calling for-in twice doesn't only memoizes once")
}
func testIndexing() {
let pattern = "\(tmpDir!)/ba{r,y,z}"
let glob = Glob(pattern: pattern)
XCTAssertEqual(glob[0], "\(tmpDir!)/bar", "indexing")
}
}
@efirestone
Copy link

I took this even further and added globstar (**) support: https://gist.github.com/efirestone/ce01ae109e08772647eb061b3bb387c3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment