Skip to content
This repository has been archived by the owner on Oct 8, 2020. It is now read-only.

Commit

Permalink
Merge 02a0c06 into e49fd52
Browse files Browse the repository at this point in the history
  • Loading branch information
scouten committed Jul 15, 2019
2 parents e49fd52 + 02a0c06 commit 924de0c
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 0 deletions.
98 changes: 98 additions & 0 deletions lib/xgit/core/file_mode.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
defmodule Xgit.Core.FileMode do
@moduledoc ~S"""
Describes the file type as represented on disk.
"""

@typedoc ~S"""
An integer describing the file type as represented on disk.
Git uses a variation on the Unix file permissions flags to denote a file's
intended type on disk. The following values are recognized:
* `0o100644` - normal file
* `0o100755` - executable file
* `0o120000` - symbolic link
* `0o040000` - tree (subdirectory)
* `0o160000` - submodule (aka gitlink)
This module is intended to be `use`d. Doing so will create an `alias` to the module
so as to make `FileMode.t` available for typespecs and will `import` the
`is_file_mode/1` guard.
"""
@type t :: 0o100644 | 0o100755 | 0o120000 | 0o040000 | 0o160000

@doc "Mode indicating an entry is a tree (aka directory)."
@spec tree :: t
def tree, do: 0o040000

@doc "Mode indicating an entry is a symbolic link."
@spec symlink :: t
def symlink, do: 0o120000

@doc "Mode indicating an entry is a non-executable file."
@spec regular_file :: t
def regular_file, do: 0o100644

@doc "Mode indicating an entry is an executable file."
@spec executable_file :: t
def executable_file, do: 0o100755

@doc "Mode indicating an entry is a submodule commit in another repository."
@spec gitlink :: t
def gitlink, do: 0o160000

@doc "Return `true` if the file mode represents a tree."
@spec tree?(file_mode :: term) :: boolean
def tree?(file_mode)
def tree?(0o040000), do: true
def tree?(_), do: false

@doc "Return `true` if the file mode a symbolic link."
@spec symlink?(file_mode :: term) :: boolean
def symlink?(file_mode)
def symlink?(0o120000), do: true
def symlink?(_), do: false

@doc "Return `true` if the file mode represents a regular file."
@spec regular_file?(file_mode :: term) :: boolean
def regular_file?(file_mode)
def regular_file?(0o100644), do: true
def regular_file?(_), do: false

@doc "Return `true` if the file omde represents an executable file."
@spec executable_file?(file_mode :: term) :: boolean
def executable_file?(file_mode)
def executable_file?(0o100755), do: true
def executable_file?(_), do: false

@doc "Return `true` if the file mode represents a submodule commit in another repository."
@spec gitlink?(file_mode :: term) :: boolean
def gitlink?(file_mode)
def gitlink?(0o160000), do: true
def gitlink?(_), do: false

@doc ~S"""
Return `true` if the value is one of the known file mode values.
"""
@spec valid?(term) :: boolean
def valid?(0o040000), do: true
def valid?(0o120000), do: true
def valid?(0o100644), do: true
def valid?(0o100755), do: true
def valid?(0o160000), do: true
def valid?(_), do: false

@valid_file_modes [0o100644, 0o100755, 0o120000, 0o040000, 0o160000]

@doc ~S"""
This guard requires the value to be one of the known git file mode values.
"""
defguard is_file_mode(t) when t in @valid_file_modes

defmacro __using__(opts) do
quote location: :keep, bind_quoted: [opts: opts] do
alias Xgit.Core.FileMode
import Xgit.Core.FileMode, only: [is_file_mode: 1]
end
end
end
93 changes: 93 additions & 0 deletions test/xgit/core/file_mode_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
defmodule Xgit.Core.FileModeTest do
use ExUnit.Case, async: true

use Xgit.Core.FileMode

test "tree/0" do
assert FileMode.tree() == 0o040000
end

test "symlink/0" do
assert FileMode.symlink() == 0o120000
end

test "regular_file/0" do
assert FileMode.regular_file() == 0o100644
end

test "executable_file/0" do
assert FileMode.executable_file() == 0o100755
end

test "gitlink/0" do
assert FileMode.gitlink() == 0o160000
end

test "tree?/1" do
assert FileMode.tree?(FileMode.tree())
refute FileMode.tree?(FileMode.tree() + 1)
end

test "symlink?/1" do
assert FileMode.symlink?(FileMode.symlink())
refute FileMode.symlink?(FileMode.symlink() + 1)
end

test "regular_file?/1" do
assert FileMode.regular_file?(FileMode.regular_file())
refute FileMode.regular_file?(FileMode.regular_file() + 1)
end

test "executable_file?/1" do
assert FileMode.executable_file?(FileMode.executable_file())
refute FileMode.executable_file?(FileMode.executable_file() + 1)
end

test "gitlink?/1" do
assert FileMode.gitlink?(FileMode.gitlink())
refute FileMode.gitlink?(FileMode.gitlink() + 1)
end

test "valid?/1" do
assert FileMode.valid?(FileMode.tree())
refute FileMode.valid?(FileMode.tree() + 1)

assert FileMode.valid?(FileMode.symlink())
refute FileMode.valid?(FileMode.symlink() + 1)

assert FileMode.valid?(FileMode.regular_file())
refute FileMode.valid?(FileMode.regular_file() + 1)

assert FileMode.valid?(FileMode.executable_file())
refute FileMode.valid?(FileMode.executable_file() + 1)

assert FileMode.valid?(FileMode.gitlink())
refute FileMode.valid?(FileMode.gitlink() + 1)
end

@valid_file_modes [0o040000, 0o120000, 0o100644, 0o100755, 0o160000]

defp accepted_file_mode?(t) when is_file_mode(t), do: true
defp accepted_file_mode?(_), do: false

describe "is_file_mode/1" do
test "accepts known file modes" do
for t <- @valid_file_modes do
assert accepted_file_mode?(t)
end
end

test "rejects invalid values" do
refute accepted_file_mode?(:mumble)
refute accepted_file_mode?(0)
refute accepted_file_mode?(1)
refute accepted_file_mode?(0o100645)
refute accepted_file_mode?("blob")
refute accepted_file_mode?('blob')
refute accepted_file_mode?(%{blob: true})
refute accepted_file_mode?({:blob})
refute accepted_file_mode?(fn -> :blob end)
refute accepted_file_mode?(self())
end
end
end

0 comments on commit 924de0c

Please sign in to comment.