csv_plus_plus 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'