11defmodule Diff.Hex.ChunkExtractor do
2- @ enforce_keys [ :file_path , :from_line , :lines_to_read , :direction ]
3- defstruct Enum . map ( @ enforce_keys , & { & 1 , nil } ) ++
4- [
5- errors: [ ] ,
6- raw: nil ,
7- parsed: nil
8- ]
2+ @ enforce_keys [ :file_path , :from_line , :to_line , :right_line ]
3+ defstruct Enum . map ( @ enforce_keys , & { & 1 , nil } ) ++ [ errors: [ ] , raw: nil , parsed: nil ]
94
105 def run ( params ) do
11- __MODULE__
12- |> struct! ( params )
13- |> parse_integers ( [ :from_line , :lines_to_read ] )
14- |> validate_direction ( )
6+ params
7+ |> cast ( )
8+ |> parse_integers ( [ :right_line , :from_line , :to_line ] )
9+ |> normalize ( [ :from_line , :right_line ] )
10+ |> validate_to_line ( )
1511 |> system_read_raw_chunk ( )
1612 |> parse_chunk ( )
17- |> remove_trailing_newline ( )
13+ |> case do
14+ % { errors: [ ] } = chunk -> { :ok , chunk }
15+ % { errors: _ } = chunk -> { :error , chunk }
16+ end
17+ end
18+
19+ defp cast ( params ) do
20+ struct_keys =
21+ struct ( __MODULE__ )
22+ |> Map . from_struct ( )
23+ |> Enum . map ( fn { key , _val } -> key end )
24+
25+ struct! ( __MODULE__ , Map . take ( params , struct_keys ) )
1826 end
1927
2028 defp parse_integers ( chunk , fields ) do
@@ -25,7 +33,7 @@ defmodule Diff.Hex.ChunkExtractor do
2533 value = chunk |> Map . get ( field ) |> parse_value ( )
2634
2735 case value do
28- :error -> % { chunk | errors: { :parse_integer , "#{ field } must be a number" } }
36+ :error -> % { chunk | errors: [ { :parse_integer , "#{ field } must be a number" } | chunk . errors ] }
2937 integer -> Map . put ( chunk , field , integer )
3038 end
3139 end
@@ -36,26 +44,39 @@ defmodule Diff.Hex.ChunkExtractor do
3644 with { int , _ } <- Integer . parse ( number ) , do: int
3745 end
3846
39- defp validate_direction ( % { direction: direction } = chunk ) when direction in [ "up" , "down" ] do
47+ defp normalize ( % { errors: [ _ | _ ] } = chunk , _ ) , do: chunk
48+
49+ defp normalize ( chunk , fields ) do
50+ Enum . reduce ( fields , chunk , & normalize_field / 2 )
51+ end
52+
53+ defp normalize_field ( field , chunk ) do
54+ case Map . fetch! ( chunk , field ) do
55+ value when value > 0 -> chunk
56+ _ -> Map . put ( chunk , field , 1 )
57+ end
58+ end
59+
60+ defp validate_to_line ( % { errors: [ _ | _ ] } = chunk ) , do: chunk
61+
62+ defp validate_to_line ( % { from_line: from_line , to_line: to_line } = chunk )
63+ when from_line < to_line do
4064 chunk
4165 end
4266
43- defp validate_direction ( chunk ) do
44- error = { :direction , "direction must be either \" up \" or \" down \" " }
67+ defp validate_to_line ( chunk ) do
68+ error = { :param , "from_line parameter must be less than to_line " }
4569 % { chunk | errors: [ error | chunk . errors ] }
4670 end
4771
4872 defp system_read_raw_chunk ( % { errors: [ _ | _ ] } = chunk ) , do: chunk
4973
5074 defp system_read_raw_chunk ( chunk ) do
51- chunk_sh = Application . app_dir ( :diff , [ "priv" , "chunk.sh" ] )
52-
5375 path = chunk . file_path
5476 from_line = to_string ( chunk . from_line )
55- lines_to_read = to_string ( chunk . lines_to_read )
56- direction = chunk . direction
77+ to_line = to_string ( chunk . to_line )
5778
58- case System . cmd ( chunk_sh , [ path , from_line , lines_to_read , direction ] , stderr_to_stdout: true ) do
79+ case System . cmd ( "sed" , [ "-n" , " #{ from_line } , #{ to_line } p" , path ] , stderr_to_stdout: true ) do
5980 { raw_chunk , 0 } ->
6081 % { chunk | raw: raw_chunk }
6182
@@ -71,42 +92,21 @@ defmodule Diff.Hex.ChunkExtractor do
7192 parsed =
7293 chunk . raw
7394 |> String . split ( "\n " )
74- |> Enum . map ( fn line -> % { line_text: line } end )
75-
76- set_line_numbers ( % { chunk | parsed: parsed } )
95+ |> remove_trailing_newline ( )
96+ |> Enum . with_index ( chunk . from_line )
97+ |> Enum . with_index ( chunk . right_line )
98+ # prepend line with whitespace to compensate the space introduced by leading + and - in diffs
99+ |> Enum . map ( fn { { line , from } , to } ->
100+ % { text: " " <> line , from_line_number: from , to_line_number: to }
101+ end )
102+
103+ % { chunk | parsed: parsed }
77104 end
78105
79- defp set_line_numbers ( % { direction: "down" } = chunk ) do
80- % { chunk | parsed: parsed_with_line_numbers ( chunk . parsed , chunk . from_line ) }
81- end
82-
83- defp set_line_numbers ( % { direction: "up" } = chunk ) do
84- offset = chunk . from_line - length ( chunk . parsed ) + 1
85-
86- % { chunk | parsed: parsed_with_line_numbers ( chunk . parsed , offset ) }
87- end
88-
89- defp parsed_with_line_numbers ( parsed_chunk , starting_number ) when is_binary ( starting_number ) do
90- parsed_with_line_numbers ( parsed_chunk , starting_number )
91- end
92-
93- defp parsed_with_line_numbers ( parsed_chunk , starting_number ) do
94- parsed_chunk
95- |> Enum . with_index ( starting_number )
96- |> Enum . map ( fn { line , line_number } -> Map . put_new ( line , :line_number , line_number ) end )
97- end
98-
99- defp remove_trailing_newline ( % { errors: [ _ | _ ] } = chunk ) , do: { :error , chunk }
100-
101- defp remove_trailing_newline ( chunk ) do
102- [ trailing_line | reversed_tail ] = Enum . reverse ( chunk . parsed )
103-
104- chunk =
105- case trailing_line do
106- % { line_text: "" } -> % { chunk | parsed: Enum . reverse ( reversed_tail ) }
107- % { line_text: _text } -> chunk
108- end
109-
110- { :ok , chunk }
106+ defp remove_trailing_newline ( lines ) do
107+ lines
108+ |> Enum . reverse ( )
109+ |> tl ( )
110+ |> Enum . reverse ( )
111111 end
112112end
0 commit comments