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

Commit

Permalink
Implement Xgit.Core.Commit.from_object/1. (#204)
Browse files Browse the repository at this point in the history
* Implement Xgit.Core.Commit.from_object/1.

* Tighten up testing. Add link to TO DO for testing interop with command-line git.
  • Loading branch information
scouten committed Oct 16, 2019
1 parent 64822b3 commit 77153c6
Show file tree
Hide file tree
Showing 2 changed files with 592 additions and 0 deletions.
79 changes: 79 additions & 0 deletions lib/xgit/core/commit.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ defmodule Xgit.Core.Commit do
@moduledoc ~S"""
Represents a git `commit` object in memory.
"""
alias Xgit.Core.ContentSource
alias Xgit.Core.Object
alias Xgit.Core.ObjectId
alias Xgit.Core.PersonIdent

import Xgit.Util.ForceCoverage
import Xgit.Util.ParseHeader, only: [next_header: 1]

@typedoc ~S"""
This struct describes a single `commit` object so it can be manipulated in memory.
Expand All @@ -18,6 +20,9 @@ defmodule Xgit.Core.Commit do
* `:author`: (`Xgit.Core.PersonIdent`) author of this commit
* `:committer`: (`Xgit.Core.PersonIdent`) committer for this commit
* `:message`: (bytelist) user-entered commit message (encoding unspecified)
**TO DO:** Support signatures and other extensions.
https://github.com/elixir-git/xgit/issues/202
"""
@type t :: %__MODULE__{
tree: ObjectId.t(),
Expand Down Expand Up @@ -53,6 +58,77 @@ defmodule Xgit.Core.Commit do

def valid?(_), do: cover(false)

@typedoc ~S"""
Error response codes returned by `from_object/1`.
"""
@type from_object_reason :: :not_a_commit | :invalid_commit

@doc ~S"""
Renders a commit structure from an `Xgit.Core.Object`.
## Return Values
`{:ok, commit}` if the object contains a valid `commit` object.
`{:error, :not_a_commit}` if the object contains an object of a different type.
`{:error, :invalid_commit}` if the object says that is of type `commit`, but
can not be parsed as such.
"""
@spec from_object(object :: Object.t()) :: {:ok, commit :: t} | {:error, from_object_reason}
def from_object(object)

def from_object(%Object{type: :commit, content: content} = _object) do
content
|> ContentSource.stream()
|> Enum.to_list()
|> from_object_internal()
end

def from_object(%Object{} = _object), do: cover({:error, :not_a_commit})

defp from_object_internal(data) do
with {:tree, {'tree', tree_id_str, data}} <- {:tree, next_header(data)},
{:tree_id, {tree_id, []}} <- {:tree_id, ObjectId.from_hex_charlist(tree_id_str)},
{:parents, {parents, data}} when is_list(data) <-
{:parents, read_parents(data, [])},
{:author, {'author', author_str, data}} <- {:author, next_header(data)},
{:author_id, %PersonIdent{} = author} <-
{:author_id, PersonIdent.from_byte_list(author_str)},
{:committer, {'committer', committer_str, data}} <-
{:committer, next_header(data)},
{:committer_id, %PersonIdent{} = committer} <-
{:committer_id, PersonIdent.from_byte_list(committer_str)},
message when is_list(message) <- drop_if_lf(data) do
# TO DO: Support signatures and other extensions.
# https://github.com/elixir-git/xgit/issues/202
cover {:ok,
%__MODULE__{
tree: tree_id,
parents: parents,
author: author,
committer: committer,
message: message
}}
else
_ -> cover {:error, :invalid_commit}
end
end

defp read_parents(data, parents_acc) do
with {'parent', parent_id, next_data} <- next_header(data),
{:parent_id, {parent_id, []}} <- {:parent_id, ObjectId.from_hex_charlist(parent_id)} do
read_parents(next_data, [parent_id | parents_acc])
else
{:parent_id, _} -> cover :error
_ -> cover {Enum.reverse(parents_acc), data}
end
end

defp drop_if_lf([10 | data]), do: cover(data)
defp drop_if_lf([]), do: cover([])
defp drop_if_lf(_), do: cover(:error)

@doc ~S"""
Renders this commit structure into a corresponding `Xgit.Core.Object`.
Expand Down Expand Up @@ -89,6 +165,9 @@ defmodule Xgit.Core.Commit do
'\n' ++
message

# TO DO: Support signatures and other extensions.
# https://github.com/elixir-git/xgit/issues/202

%Object{
type: :commit,
content: rendered_commit,
Expand Down
Loading

0 comments on commit 77153c6

Please sign in to comment.