csv_plus_plus 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -5
  3. data/{CHANGELOG.md → docs/CHANGELOG.md} +25 -0
  4. data/lib/csv_plus_plus/a1_reference.rb +202 -0
  5. data/lib/csv_plus_plus/benchmarked_compiler.rb +70 -20
  6. data/lib/csv_plus_plus/cell.rb +29 -41
  7. data/lib/csv_plus_plus/cli.rb +53 -80
  8. data/lib/csv_plus_plus/cli_flag.rb +71 -71
  9. data/lib/csv_plus_plus/color.rb +32 -7
  10. data/lib/csv_plus_plus/compiler.rb +98 -66
  11. data/lib/csv_plus_plus/entities/ast_builder.rb +30 -39
  12. data/lib/csv_plus_plus/entities/boolean.rb +26 -10
  13. data/lib/csv_plus_plus/entities/builtins.rb +66 -24
  14. data/lib/csv_plus_plus/entities/date.rb +42 -6
  15. data/lib/csv_plus_plus/entities/entity.rb +17 -69
  16. data/lib/csv_plus_plus/entities/entity_with_arguments.rb +44 -0
  17. data/lib/csv_plus_plus/entities/function.rb +34 -11
  18. data/lib/csv_plus_plus/entities/function_call.rb +49 -10
  19. data/lib/csv_plus_plus/entities/has_identifier.rb +19 -0
  20. data/lib/csv_plus_plus/entities/number.rb +30 -11
  21. data/lib/csv_plus_plus/entities/reference.rb +77 -0
  22. data/lib/csv_plus_plus/entities/runtime_value.rb +43 -13
  23. data/lib/csv_plus_plus/entities/string.rb +23 -7
  24. data/lib/csv_plus_plus/entities.rb +7 -16
  25. data/lib/csv_plus_plus/error/cli_error.rb +17 -0
  26. data/lib/csv_plus_plus/error/compiler_error.rb +17 -0
  27. data/lib/csv_plus_plus/error/error.rb +25 -2
  28. data/lib/csv_plus_plus/error/formula_syntax_error.rb +12 -12
  29. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +34 -12
  30. data/lib/csv_plus_plus/error/modifier_validation_error.rb +21 -27
  31. data/lib/csv_plus_plus/error/positional_error.rb +15 -0
  32. data/lib/csv_plus_plus/error/writer_error.rb +8 -0
  33. data/lib/csv_plus_plus/error.rb +5 -1
  34. data/lib/csv_plus_plus/error_formatter.rb +111 -0
  35. data/lib/csv_plus_plus/google_api_client.rb +25 -10
  36. data/lib/csv_plus_plus/lexer/racc_lexer.rb +144 -0
  37. data/lib/csv_plus_plus/lexer/tokenizer.rb +58 -17
  38. data/lib/csv_plus_plus/lexer.rb +64 -1
  39. data/lib/csv_plus_plus/modifier/conditional_formatting.rb +1 -0
  40. data/lib/csv_plus_plus/modifier/data_validation.rb +138 -0
  41. data/lib/csv_plus_plus/modifier/expand.rb +78 -0
  42. data/lib/csv_plus_plus/modifier/google_sheet_modifier.rb +133 -0
  43. data/lib/csv_plus_plus/modifier/modifier.rb +222 -0
  44. data/lib/csv_plus_plus/modifier/modifier_validator.rb +243 -0
  45. data/lib/csv_plus_plus/modifier/rubyxl_modifier.rb +84 -0
  46. data/lib/csv_plus_plus/modifier.rb +89 -160
  47. data/lib/csv_plus_plus/options/file_options.rb +49 -0
  48. data/lib/csv_plus_plus/options/google_sheets_options.rb +42 -0
  49. data/lib/csv_plus_plus/options/options.rb +97 -0
  50. data/lib/csv_plus_plus/options.rb +34 -77
  51. data/lib/csv_plus_plus/parser/cell_value.tab.rb +66 -67
  52. data/lib/csv_plus_plus/parser/code_section.tab.rb +86 -83
  53. data/lib/csv_plus_plus/parser/modifier.tab.rb +57 -53
  54. data/lib/csv_plus_plus/reader/csv.rb +50 -0
  55. data/lib/csv_plus_plus/reader/google_sheets.rb +129 -0
  56. data/lib/csv_plus_plus/reader/reader.rb +27 -0
  57. data/lib/csv_plus_plus/reader/rubyxl.rb +37 -0
  58. data/lib/csv_plus_plus/reader.rb +14 -0
  59. data/lib/csv_plus_plus/row.rb +53 -12
  60. data/lib/csv_plus_plus/runtime/graph.rb +68 -0
  61. data/lib/csv_plus_plus/runtime/position.rb +242 -0
  62. data/lib/csv_plus_plus/runtime/references.rb +115 -0
  63. data/lib/csv_plus_plus/runtime/runtime.rb +132 -0
  64. data/lib/csv_plus_plus/runtime/scope.rb +280 -0
  65. data/lib/csv_plus_plus/runtime.rb +34 -191
  66. data/lib/csv_plus_plus/source_code.rb +71 -0
  67. data/lib/csv_plus_plus/template.rb +71 -39
  68. data/lib/csv_plus_plus/version.rb +2 -1
  69. data/lib/csv_plus_plus/writer/csv.rb +37 -8
  70. data/lib/csv_plus_plus/writer/excel.rb +25 -5
  71. data/lib/csv_plus_plus/writer/file_backer_upper.rb +27 -13
  72. data/lib/csv_plus_plus/writer/google_sheets.rb +29 -85
  73. data/lib/csv_plus_plus/writer/google_sheets_builder.rb +179 -0
  74. data/lib/csv_plus_plus/writer/merger.rb +31 -0
  75. data/lib/csv_plus_plus/writer/open_document.rb +21 -2
  76. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +140 -42
  77. data/lib/csv_plus_plus/writer/writer.rb +42 -0
  78. data/lib/csv_plus_plus/writer.rb +79 -10
  79. data/lib/csv_plus_plus.rb +47 -18
  80. metadata +50 -21
  81. data/lib/csv_plus_plus/can_define_references.rb +0 -88
  82. data/lib/csv_plus_plus/can_resolve_references.rb +0 -8
  83. data/lib/csv_plus_plus/data_validation.rb +0 -138
  84. data/lib/csv_plus_plus/entities/cell_reference.rb +0 -60
  85. data/lib/csv_plus_plus/entities/variable.rb +0 -25
  86. data/lib/csv_plus_plus/error/syntax_error.rb +0 -58
  87. data/lib/csv_plus_plus/expand.rb +0 -20
  88. data/lib/csv_plus_plus/google_options.rb +0 -27
  89. data/lib/csv_plus_plus/graph.rb +0 -62
  90. data/lib/csv_plus_plus/lexer/lexer.rb +0 -85
  91. data/lib/csv_plus_plus/references.rb +0 -68
  92. data/lib/csv_plus_plus/scope.rb +0 -196
  93. data/lib/csv_plus_plus/validated_modifier.rb +0 -164
  94. data/lib/csv_plus_plus/writer/base_writer.rb +0 -20
  95. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +0 -147
  96. data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +0 -77
  97. data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +0 -59
@@ -1,179 +1,108 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
- module CSVPlusPlus
4
- # A container representing the operations that can be applied to a cell or row
5
- #
6
- # @attr bordercolor [Color]
7
- # @attr borders [Array<Symbol>] The borders that will be set
8
- # @attr color [Color] The background color of the cell
9
- # @attr expand [Expand] Whether this row expands into multiple rows
10
- # @attr fontcolor [Color] The font color of the cell
11
- # @attr fontfamily [::String] The font family
12
- # @attr fontsize [Integer] The font size
13
- # @attr halign [:left, :center, :right] Horizontal alignment
14
- # @attr note [::String] A note/comment on the cell
15
- # @attr numberformat [Symbol] A number format to apply to the value in the cell
16
- # @attr row_level [boolean] Is this a row modifier? If so it's values will apply to all cells in the row
17
- # (unless overridden by the cell modifier)
18
- # @attr validation [Object]
19
- # @attr valign [:top, :center, :bottom] Vertical alignment
20
- # @attr var [Symbol] The variable bound to this cell
21
- #
22
- # @attr_writer borderstyle [:hashed, :dotted, :double, :solid, :solid_medium, :solid_thick]
23
- # The style of border on the cell
24
- #
25
- # @attr_reader borders [Array<Symbol>]
26
- # @attr_reader formats [Array<Symbol>] Bold/italics/underline/strikethrough formatting
27
- class Modifier
28
- attr_accessor :bordercolor,
29
- :color,
30
- :expand,
31
- :fontcolor,
32
- :fontfamily,
33
- :fontsize,
34
- :halign,
35
- :valign,
36
- :note,
37
- :numberformat,
38
- :row_level,
39
- :validation,
40
- :var
41
- attr_reader :borders, :formats
42
- attr_writer :borderstyle
43
-
44
- # When instantiating a new object, extend it with our validation functionality.
45
- #
46
- # I'm not sure why I need to do it this way tbh, using +include ValidatedModifier+ at the
47
- # class level didn't seem to have access to the parent methods
48
- # def self.new(*args, **kwargs, &)
49
- # allocate.tap do |i|
50
- # i.__send__(:initialize, *args, **kwargs, &)
51
- # i.extend(::CSVPlusPlus::ValidatedModifier)
52
- # end
53
- # end
54
-
55
- # @param row_level [Boolean] Whether or not this modifier applies to the entire row
56
- def initialize(row_level: false)
57
- @row_level = row_level
58
- @freeze = false
59
- @borders = ::Set.new
60
- @formats = ::Set.new
61
- end
62
-
63
- # Are there any borders set?
64
- #
65
- # @return [Boolean]
66
- def any_border?
67
- !@borders.empty?
68
- end
69
-
70
- # Style of border
71
- #
72
- # @return [Symbol]
73
- def borderstyle
74
- @borderstyle || :solid
75
- end
76
-
77
- # Is this a cell-level modifier?
78
- #
79
- # @return [Boolean]
80
- def cell_level?
81
- !@row_level
82
- end
83
-
84
- # Assign a border
85
- #
86
- # @param side [:top, :left, :bottom, :right, :all]
87
- def border=(side)
88
- @borders << side
89
- end
90
-
91
- # Does this have a border along +side+?
92
- #
93
- # @param side [:top, :left, :bottom, :right, :all]
94
- #
95
- # @return [boolean]
96
- def border_along?(side)
97
- @borders.include?(:all) || @borders.include?(side)
98
- end
99
-
100
- # Does this have a border along all sides?
101
- #
102
- # @return [boolean]
103
- def border_all?
104
- @borders.include?(:all) \
105
- || (border_along?(:top) && border_along?(:bottom) && border_along?(:left) && border_along?(:right))
106
- end
107
-
108
- # Set this modifier to expand infinitely
109
- #
110
- # @return [::Expand]
111
- def expand!
112
- @expand = ::CSVPlusPlus::Expand.new if row_level?
113
- end
114
-
115
- # Set a text format (bolid, italic, underline or strikethrough)
116
- #
117
- # @param value [:bold, :italic, :underline, :strikethrough]
118
- def format=(value)
119
- @formats << value
120
- end
4
+ require_relative './modifier/conditional_formatting'
5
+ require_relative './modifier/data_validation'
6
+ require_relative './modifier/expand'
7
+ require_relative './modifier/modifier'
121
8
 
122
- # Is the given format set?
123
- #
124
- # @param type [:bold, :italic, :underline, :strikethrough]
125
- #
126
- # @return [Boolean]
127
- def formatted?(type)
128
- @formats.include?(type)
9
+ module CSVPlusPlus
10
+ # All modifier-specific logic is hidden in this module and callers should just call +#new+ on this module.
11
+ module Modifier
12
+ extend ::T::Sig
13
+
14
+ # The sides that a border can be on
15
+ class BorderSide < ::T::Enum
16
+ enums do
17
+ All = new
18
+ Top = new
19
+ Bottom = new
20
+ Left = new
21
+ Right = new
22
+ end
129
23
  end
130
24
 
131
- # Freeze the row from edits
132
- #
133
- # @return [true]
134
- def freeze!
135
- @frozen = true
25
+ # The various border styles
26
+ class BorderStyle < ::T::Enum
27
+ enums do
28
+ Dashed = new
29
+ Dotted = new
30
+ Double = new
31
+ Solid = new
32
+ SolidMedium = new
33
+ SolidThick = new
34
+ end
136
35
  end
137
36
 
138
- # Is the row frozen?
139
- #
140
- # @return [boolean]
141
- def frozen?
142
- @frozen
37
+ # The possible values for a horizontal alignment
38
+ class HorizontalAlign < ::T::Enum
39
+ enums do
40
+ Left = new
41
+ Right = new
42
+ Center = new
43
+ end
143
44
  end
144
45
 
145
- # Mark this modifer as row-level
146
- #
147
- # @return [true]
148
- def row_level!
149
- @row_level = true
46
+ # The allowed number formats
47
+ class NumberFormat < ::T::Enum
48
+ enums do
49
+ Currency = new
50
+ Date = new
51
+ DateTime = new
52
+ Number = new
53
+ Percent = new
54
+ Text = new
55
+ Time = new
56
+ Scientific = new
57
+ end
150
58
  end
151
59
 
152
- # Is this a row-level modifier?
153
- #
154
- # @return [boolean]
155
- def row_level?
156
- @row_level
60
+ # The types of formats that can be applied to text.
61
+ class TextFormat < ::T::Enum
62
+ enums do
63
+ Bold = new
64
+ Italic = new
65
+ Strikethrough = new
66
+ Underline = new
67
+ end
157
68
  end
158
69
 
159
- # @return [::String]
160
- def to_s
161
- # TODO... I dunno, not sure how to manage this
162
- "Modifier(row_level: #{@row_level} halign: #{@halign} valign: #{@valign} format: #{@formats} " \
163
- "font_size: #{@font_size})"
70
+ # The possible values for a horizontal alignment
71
+ class VerticalAlign < ::T::Enum
72
+ enums do
73
+ Top = new
74
+ Bottom = new
75
+ Center = new
76
+ end
164
77
  end
165
78
 
166
- # Create a new modifier instance, with all values defaulted from +other+
167
- #
168
- # @param other [Modifier]
169
- def take_defaults_from!(other)
170
- other.instance_variables.each do |property|
171
- # don't propagate row-specific values
172
- next if property == :@row_level
173
-
174
- value = other.instance_variable_get(property)
175
- instance_variable_set(property, value.clone)
79
+ sig do
80
+ params(
81
+ options: ::CSVPlusPlus::Options::Options,
82
+ row_level: ::T::Boolean
83
+ ).returns(::CSVPlusPlus::Modifier::Modifier)
84
+ end
85
+ # Return a +Modifier+ with the proper validation and helper functions attached for the given output
86
+ #
87
+ # @param options [boolean] is this a row level modifier? (otherwise cell-level)
88
+ # @param row_level [boolean] is this a row level modifier? (otherwise cell-level)
89
+ #
90
+ # @return [ValidatedModifier]
91
+ def self.new(options, row_level: false)
92
+ output_format = options.output_format
93
+ case output_format
94
+ when ::CSVPlusPlus::Options::OutputFormat::CSV, ::CSVPlusPlus::Options::OutputFormat::OpenDocument
95
+ ::CSVPlusPlus::Modifier::Modifier.new(row_level:)
96
+ when ::CSVPlusPlus::Options::OutputFormat::Excel
97
+ ::CSVPlusPlus::Modifier::RubyXLModifier.new(row_level:)
98
+ when ::CSVPlusPlus::Options::OutputFormat::GoogleSheets
99
+ ::CSVPlusPlus::Modifier::GoogleSheetModifier.new(row_level:)
100
+ else ::T.absurd(output_format)
176
101
  end
177
102
  end
178
103
  end
179
104
  end
105
+
106
+ require_relative './modifier/google_sheet_modifier'
107
+ require_relative './modifier/modifier_validator'
108
+ require_relative './modifier/rubyxl_modifier'
@@ -0,0 +1,49 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Options
6
+ # The options that are specific for compiling to a file
7
+ #
8
+ # @attr output_filename [Pathname] The file to write our compiled results to
9
+ class FileOptions < ::CSVPlusPlus::Options::Options
10
+ extend ::T::Sig
11
+ extend ::T::Helpers
12
+
13
+ sig { returns(::Pathname) }
14
+ attr_accessor :output_filename
15
+
16
+ sig { params(sheet_name: ::String, output_filename: ::String).void }
17
+ # Initialize an +Options+ object for writing to a file
18
+ def initialize(sheet_name, output_filename)
19
+ super(sheet_name)
20
+
21
+ @output_filename = ::T.let(::Pathname.new(output_filename), ::Pathname)
22
+ end
23
+
24
+ sig { override.returns(::CSVPlusPlus::Options::OutputFormat) }
25
+ # Given the options, figure out which type of +OutputFormat+ we'll be writing to
26
+ #
27
+ # @return [Options::OutputFormat]
28
+ def output_format
29
+ case output_filename.extname
30
+ when '.csv' then ::CSVPlusPlus::Options::OutputFormat::CSV
31
+ when '.ods' then ::CSVPlusPlus::Options::OutputFormat::OpenDocument
32
+ when /\.xl(sx|sm|tx|tm)$/ then ::CSVPlusPlus::Options::OutputFormat::Excel
33
+ else raise(::CSVPlusPlus::Error::CLIError, "Unsupported file extension: #{output_filename}")
34
+ end
35
+ end
36
+
37
+ sig { override.returns(::String) }
38
+ # Verbose summary for options specific to compiling to a file
39
+ #
40
+ # @return [String]
41
+ def verbose_summary
42
+ shared_summary(
43
+ <<~OUTPUT)
44
+ > Output filename | #{output_filename}
45
+ OUTPUT
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,42 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Options
6
+ # The Google-specific options a user can supply.
7
+ #
8
+ # @attr sheet_id [String] The ID of the Google Sheet to write to.
9
+ class GoogleSheetsOptions < Options
10
+ extend ::T::Sig
11
+
12
+ sig { returns(::String) }
13
+ attr_reader :sheet_id
14
+
15
+ sig { params(sheet_name: ::String, sheet_id: ::String).void }
16
+ # @param sheet_name [String] The name of the sheet
17
+ # @param sheet_id [String] The unique ID Google uses to reference the sheet
18
+ def initialize(sheet_name, sheet_id)
19
+ super(sheet_name)
20
+
21
+ @sheet_id = sheet_id
22
+ end
23
+
24
+ sig { override.returns(::CSVPlusPlus::Options::OutputFormat) }
25
+ # @return [OutputFormat]
26
+ def output_format
27
+ ::CSVPlusPlus::Options::OutputFormat::GoogleSheets
28
+ end
29
+
30
+ sig { override.returns(::String) }
31
+ # Format a string with a verbose description of Google-specific options
32
+ #
33
+ # @return [String]
34
+ def verbose_summary
35
+ shared_summary(
36
+ <<~SUMMARY)
37
+ > Sheet ID | #{@sheet_id}
38
+ SUMMARY
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,97 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Options
6
+ # The options a user can supply (via CLI flags)
7
+ #
8
+ # @attr backup [Boolean] Create a backup of the spreadsheet before writing
9
+ # @attr create_if_not_exists [Boolean] Create the spreadsheet if it does not exist?
10
+ # @attr key_values [Hash] Additional variables that can be supplied to the template
11
+ # @attr offset [Array<Integer>] An [x, y] offset (array with two integers)
12
+ # @attr sheet_name [String] The name of the spreadsheet to write to
13
+ # @attr verbose [Boolean] Include extra verbose output?
14
+ class Options
15
+ extend ::T::Sig
16
+ extend ::T::Helpers
17
+
18
+ abstract!
19
+
20
+ sig { returns(::T::Boolean) }
21
+ attr_accessor :backup
22
+
23
+ sig { returns(::T::Boolean) }
24
+ attr_accessor :create_if_not_exists
25
+
26
+ sig { returns(::T::Hash[::Symbol, ::CSVPlusPlus::Entities::Entity]) }
27
+ attr_accessor :key_values
28
+
29
+ sig { returns(::T::Array[::Integer]) }
30
+ attr_accessor :offset
31
+
32
+ sig { returns(::String) }
33
+ attr_accessor :sheet_name
34
+
35
+ sig { returns(::T::Boolean) }
36
+ attr_accessor :verbose
37
+
38
+ sig { params(sheet_name: ::String).void }
39
+ # Initialize a defaul +Options+ object
40
+ def initialize(sheet_name)
41
+ @sheet_name = sheet_name
42
+ @offset = ::T.let([0, 0], ::T::Array[::Integer])
43
+ @create_if_not_exists = ::T.let(false, ::T::Boolean)
44
+ @key_values = ::T.let({}, ::T::Hash[::Symbol, ::CSVPlusPlus::Entities::Entity])
45
+ @verbose = ::T.let(false, ::T::Boolean)
46
+ @backup = ::T.let(false, ::T::Boolean)
47
+ end
48
+
49
+ sig { abstract.returns(::CSVPlusPlus::Options::OutputFormat) }
50
+ # Given the options, figure out which type of +OutputFormat+ we'll be writing to
51
+ #
52
+ # @return [Options::OutputFormat]
53
+ def output_format; end
54
+
55
+ sig { abstract.returns(::String) }
56
+ # Return a string with a verbose description of what we're doing with the options
57
+ #
58
+ # @return [String]
59
+ def verbose_summary; end
60
+
61
+ protected
62
+
63
+ sig { params(str: ::String).returns(::String) }
64
+ # Return a string with a verbose description of what we're doing with the options
65
+ #
66
+ # @return [String]
67
+ def shared_summary(str)
68
+ <<~SUMMARY
69
+ #{summary_divider}
70
+
71
+ # csv++ Command Options
72
+
73
+ > Sheet name | #{@sheet_name}
74
+ > Create sheet if it does not exist? | #{@create_if_not_exists}
75
+ > Spreadsheet row-offset | #{@offset[0]}
76
+ > Spreadsheet cell-offset | #{@offset[1]}
77
+ > User-supplied key-values | #{@key_values}
78
+ > Verbose | #{@verbose}
79
+ > Backup | #{@backup}
80
+
81
+ ## Output Options
82
+
83
+ #{str}
84
+
85
+ #{summary_divider}
86
+ SUMMARY
87
+ end
88
+
89
+ private
90
+
91
+ sig { returns(::String) }
92
+ def summary_divider
93
+ '========================================================================='
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,88 +1,45 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
- require_relative './cli_flag'
4
- require_relative './google_options'
5
-
6
4
  module CSVPlusPlus
7
- # The options a user can supply (via CLI flags)
8
- #
9
- # @attr backup [boolean] Create a backup of the spreadsheet before writing
10
- # @attr create_if_not_exists [boolean] Create the spreadsheet if it does not exist?
11
- # @attr key_values [Hash] Additional variables that can be supplied to the template
12
- # @attr offset [Array<Integer>] An [x, y] offset (array with two integers)
13
- # @attr output_filename [String] The file to write our compiled results to
14
- # @attr sheet_name [String] The name of the spreadsheet to write to
15
- # @attr verbose [boolean] Include extra verbose output?
16
- # @attr_reader google [GoogleOptions] Options that are specific to the Google Sheets writer
17
- class Options
18
- attr_accessor :backup, :create_if_not_exists, :key_values, :offset, :output_filename, :sheet_name, :verbose
19
- attr_reader :google
20
-
21
- # initialize
22
- def initialize
23
- @offset = [0, 0]
24
- @create_if_not_exists = false
25
- @key_values = {}
26
- @verbose = false
27
- @backup = false
5
+ # Options that a user can supply - either specific for compiling to a file (xlsx, csv) or Google Sheets
6
+ module Options
7
+ extend ::T::Sig
8
+
9
+ # The supported output formats. We use this to dispatch flow in several places
10
+ class OutputFormat < ::T::Enum
11
+ enums do
12
+ CSV = new
13
+ Excel = new
14
+ GoogleSheets = new
15
+ OpenDocument = new
16
+ end
28
17
  end
29
18
 
30
- # Set the Google Sheet ID
31
- #
32
- # @param sheet_id [String] The identifier used by Google's API to reference the sheet. You can find it in the URL
33
- # for the sheet
34
- #
35
- # @return [String]
36
- def google_sheet_id=(sheet_id)
37
- @google = ::CSVPlusPlus::GoogleOptions.new(sheet_id)
19
+ sig do
20
+ params(flags: ::T::Hash[::Symbol, ::String], input_filename: ::Pathname).returns(::CSVPlusPlus::Options::Options)
38
21
  end
39
-
40
- # Returns an error string or nil if there are no validation problems
22
+ # Use the given +flags+ to determine if we're dealing with either a Google Sheets or file-based
23
+ # compilation and build an +Options+ instance accordingly.
41
24
  #
42
- # @return [String, nil]
43
- def validate
44
- return if @google || @output_filename
45
-
46
- 'You must supply either a Google Sheet ID or an output file'
47
- end
48
-
49
- # Return a string with a verbose description of what we're doing with the options
25
+ # @param flags [Hash<Symbol, String>]
26
+ # @param input_filename [Pathname]
50
27
  #
51
- # @return [String]
52
- def verbose_summary
53
- <<~SUMMARY
54
- #{summary_divider}
55
-
56
- # csv++ Command Options
57
-
58
- > Input filename | #{@filename}
59
- > Sheet name | #{@sheet_name}
60
- > Create sheet if it does not exist? | #{@create_if_not_exists}
61
- > Spreadsheet row-offset | #{@offset[0]}
62
- > Spreadsheet cell-offset | #{@offset[1]}
63
- > User-supplied key-values | #{@key_values}
64
- > Verbose | #{@verbose}
65
-
66
- ## Output Options
67
-
68
- > Backup | #{@backup}
69
- > Output filename | #{@output_filename}
70
-
71
- #{@google&.verbose_summary || ''}
72
- #{summary_divider}
73
- SUMMARY
74
- end
75
-
76
- # @return [String]
77
- def to_s
78
- "Options(create_if_not_exists: #{@create_if_not_exists}, google: #{@google}, key_values: #{@key_values}, " \
79
- "offset: #{@offset}, sheet_name: #{@sheet_name}, verbose: #{@verbose})"
80
- end
81
-
82
- private
83
-
84
- def summary_divider
85
- '========================================================================='
28
+ # @return [Options::Options]
29
+ def self.from_cli_flags(flags, input_filename)
30
+ sheet_name = flags[:'sheet-name'] || input_filename.sub_ext('').to_s
31
+ if (google_sheet_id = flags[:'google-sheet-id'])
32
+ ::CSVPlusPlus::Options::GoogleSheetsOptions.new(sheet_name, google_sheet_id)
33
+ elsif (output_filename = flags[:output])
34
+ ::CSVPlusPlus::Options::FileOptions.new(sheet_name, output_filename)
35
+ else
36
+ raise(::CSVPlusPlus::Error::CLIError, 'You must supply either -o/--output or -g/-google-sheet-id')
37
+ end
86
38
  end
87
39
  end
88
40
  end
41
+
42
+ require_relative './options/options'
43
+
44
+ require_relative './options/file_options'
45
+ require_relative './options/google_sheets_options'