Skip to content

Commit 3a8450a

Browse files
krkkalimpfard
authored andcommitted
Spreadsheet: Port XSV writer to Core::Stream
1 parent 5793f77 commit 3a8450a

File tree

5 files changed

+74
-120
lines changed

5 files changed

+74
-120
lines changed

Userland/Applications/Spreadsheet/ExportDialog.cpp

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
#include <AK/DeprecatedString.h>
1111
#include <AK/JsonArray.h>
1212
#include <AK/LexicalPath.h>
13-
#include <AK/MemoryStream.h>
1413
#include <Applications/Spreadsheet/CSVExportGML.h>
15-
#include <LibCore/File.h>
1614
#include <LibCore/FileStream.h>
15+
#include <LibCore/MemoryStream.h>
1716
#include <LibCore/StandardPaths.h>
1817
#include <LibGUI/Application.h>
1918
#include <LibGUI/CheckBox.h>
@@ -90,7 +89,7 @@ CSVExportDialogPage::CSVExportDialogPage(Sheet const& sheet)
9089
update_preview();
9190
}
9291

93-
auto CSVExportDialogPage::make_writer(OutputStream& stream) -> ErrorOr<XSV>
92+
auto CSVExportDialogPage::make_writer(Core::Stream::Handle<Core::Stream::Stream> stream) -> ErrorOr<NonnullOwnPtr<XSV>>
9493
{
9594
auto delimiter = TRY([this]() -> ErrorOr<DeprecatedString> {
9695
if (m_delimiter_other_radio->is_checked()) {
@@ -152,23 +151,22 @@ auto CSVExportDialogPage::make_writer(OutputStream& stream) -> ErrorOr<XSV>
152151
if (should_quote_all_fields)
153152
behaviors = behaviors | Writer::WriterBehavior::QuoteAll;
154153

155-
return XSV(stream, m_data, move(traits), *headers, behaviors);
154+
return try_make<XSV>(move(stream), m_data, move(traits), *headers, behaviors);
156155
}
157156

158157
void CSVExportDialogPage::update_preview()
159158
{
160-
DuplexMemoryStream memory_stream;
161-
auto writer_or_error = make_writer(memory_stream);
162-
if (!writer_or_error.is_error()) {
163-
m_data_preview_text_editor->set_text(DeprecatedString::formatted("Cannot update preview: {}", writer_or_error.error()));
164-
return;
165-
}
166-
auto writer = writer_or_error.release_value();
167-
168-
writer.generate_preview();
169-
auto buffer = memory_stream.copy_into_contiguous_buffer();
170-
m_data_preview_text_editor->set_text(StringView(buffer));
171-
m_data_preview_text_editor->update();
159+
auto maybe_error = [this]() -> ErrorOr<void> {
160+
Core::Stream::AllocatingMemoryStream memory_stream;
161+
auto writer = TRY(make_writer(Core::Stream::Handle<Core::Stream::Stream>(memory_stream)));
162+
TRY(writer->generate_preview());
163+
auto buffer = TRY(memory_stream.read_until_eof());
164+
m_data_preview_text_editor->set_text(StringView(buffer));
165+
m_data_preview_text_editor->update();
166+
return {};
167+
}();
168+
if (maybe_error.is_error())
169+
m_data_preview_text_editor->set_text(DeprecatedString::formatted("Cannot update preview: {}", maybe_error.error()));
172170
}
173171

174172
ErrorOr<void> ExportDialog::make_and_run_for(StringView mime, Core::File& file, Workbook& workbook)
@@ -188,12 +186,9 @@ ErrorOr<void> ExportDialog::make_and_run_for(StringView mime, Core::File& file,
188186
if (wizard->exec() != GUI::Dialog::ExecResult::OK)
189187
return Error::from_string_literal("CSV Export was cancelled");
190188

191-
auto file_stream = Core::OutputFileStream(file);
192-
auto writer = TRY(page.make_writer(file_stream));
193-
writer.generate();
194-
if (writer.has_error())
195-
return Error::from_string_literal("CSV Export failed");
196-
return {};
189+
auto file_stream = TRY(try_make<Core::Stream::WrappedAKOutputStream>(TRY(try_make<Core::OutputFileStream>(file))));
190+
auto writer = TRY(page.make_writer(move(file_stream)));
191+
return writer->generate();
197192
};
198193

199194
auto export_worksheet = [&]() -> ErrorOr<void> {

Userland/Applications/Spreadsheet/ExportDialog.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct CSVExportDialogPage {
2323
explicit CSVExportDialogPage(Sheet const&);
2424

2525
NonnullRefPtr<GUI::WizardPage> page() { return *m_page; }
26-
ErrorOr<XSV> make_writer(OutputStream&);
26+
ErrorOr<NonnullOwnPtr<XSV>> make_writer(Core::Stream::Handle<Core::Stream::Stream>);
2727

2828
protected:
2929
void update_preview();

Userland/Applications/Spreadsheet/Writers/CSV.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ namespace Writer {
1515
template<typename ContainerType>
1616
class CSV : public XSV<ContainerType> {
1717
public:
18-
CSV(OutputStream& output, ContainerType const& data, Vector<StringView> const& headers = {}, WriterBehavior behaviors = default_behaviors())
19-
: XSV<ContainerType>(output, data, { ",", "\"", WriterTraits::Repeat }, headers, behaviors)
18+
CSV(Core::Stream::Handle<Core::Stream::Stream> output, ContainerType const& data, Vector<StringView> headers = {}, WriterBehavior behaviors = default_behaviors())
19+
: XSV<ContainerType>(move(output), data, { ",", "\"", WriterTraits::Repeat }, move(headers), behaviors)
2020
{
2121
}
2222
};

Userland/Applications/Spreadsheet/Writers/Test/TestXSVWriter.cpp

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
#include "../CSV.h"
1010
#include "../XSV.h"
11-
#include <AK/MemoryStream.h>
11+
#include <LibCore/MemoryStream.h>
1212

1313
TEST_CASE(can_write)
1414
{
@@ -18,17 +18,17 @@ TEST_CASE(can_write)
1818
{ 7, 8, 9 },
1919
};
2020

21-
auto buffer = ByteBuffer::create_uninitialized(1024).release_value();
22-
OutputMemoryStream stream { buffer };
23-
24-
Writer::CSV csv(stream, data);
21+
Core::Stream::AllocatingMemoryStream stream;
22+
auto csv = Writer::CSV(Core::Stream::Handle<Core::Stream::Stream>(stream), data);
23+
MUST(csv.generate());
2524

2625
auto expected_output = R"~(1,2,3
2726
4,5,6
2827
7,8,9
29-
)~";
28+
)~"sv;
3029

31-
EXPECT_EQ(StringView { stream.bytes() }, expected_output);
30+
auto buffer = MUST(stream.read_until_eof());
31+
EXPECT_EQ(StringView { buffer.bytes() }, expected_output);
3232
}
3333

3434
TEST_CASE(can_write_with_header)
@@ -39,18 +39,18 @@ TEST_CASE(can_write_with_header)
3939
{ 7, 8, 9 },
4040
};
4141

42-
auto buffer = ByteBuffer::create_uninitialized(1024).release_value();
43-
OutputMemoryStream stream { buffer };
44-
45-
Writer::CSV csv(stream, data, { "A"sv, "B\""sv, "C"sv });
42+
Core::Stream::AllocatingMemoryStream stream;
43+
auto csv = Writer::CSV(Core::Stream::Handle<Core::Stream::Stream>(stream), data, { "A"sv, "B\""sv, "C"sv });
44+
MUST(csv.generate());
4645

4746
auto expected_output = R"~(A,"B""",C
4847
1,2,3
4948
4,5,6
5049
7,8,9
51-
)~";
50+
)~"sv;
5251

53-
EXPECT_EQ(StringView { stream.bytes() }, expected_output);
52+
auto buffer = MUST(stream.read_until_eof());
53+
EXPECT_EQ(StringView { buffer.bytes() }, expected_output);
5454
}
5555

5656
TEST_CASE(can_write_with_different_behaviors)
@@ -60,15 +60,15 @@ TEST_CASE(can_write_with_different_behaviors)
6060
{ "We\"ll", "Hello,", " Friends" },
6161
};
6262

63-
auto buffer = ByteBuffer::create_uninitialized(1024).release_value();
64-
OutputMemoryStream stream { buffer };
65-
66-
Writer::CSV csv(stream, data, { "A"sv, "B\""sv, "C"sv }, Writer::WriterBehavior::QuoteOnlyInFieldStart | Writer::WriterBehavior::WriteHeaders);
63+
Core::Stream::AllocatingMemoryStream stream;
64+
auto csv = Writer::CSV(Core::Stream::Handle<Core::Stream::Stream>(stream), data, { "A"sv, "B\""sv, "C"sv }, Writer::WriterBehavior::QuoteOnlyInFieldStart | Writer::WriterBehavior::WriteHeaders);
65+
MUST(csv.generate());
6766

6867
auto expected_output = R"~(A,B",C
6968
Well,Hello",Friends
7069
We"ll,"Hello,", Friends
71-
)~";
70+
)~"sv;
7271

73-
EXPECT_EQ(StringView { stream.bytes() }, expected_output);
72+
auto buffer = MUST(stream.read_until_eof());
73+
EXPECT_EQ(StringView { buffer.bytes() }, expected_output);
7474
}

Userland/Applications/Spreadsheet/Writers/XSV.h

Lines changed: 35 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
#include <AK/DeprecatedString.h>
1010
#include <AK/GenericLexer.h>
1111
#include <AK/OwnPtr.h>
12-
#include <AK/Stream.h>
1312
#include <AK/StringView.h>
1413
#include <AK/Types.h>
1514
#include <AK/Vector.h>
15+
#include <LibCore/Stream.h>
1616

1717
namespace Writer {
1818

@@ -34,17 +34,6 @@ struct WriterTraits {
3434
} quote_escape { Repeat };
3535
};
3636

37-
#define ENUMERATE_WRITE_ERRORS() \
38-
E(None, "No errors") \
39-
E(NonConformingColumnCount, "Header count does not match given column count") \
40-
E(InternalError, "Internal error")
41-
42-
enum class WriteError {
43-
#define E(name, _) name,
44-
ENUMERATE_WRITE_ERRORS()
45-
#undef E
46-
};
47-
4837
constexpr WriterBehavior default_behaviors()
4938
{
5039
return WriterBehavior::None;
@@ -53,107 +42,84 @@ constexpr WriterBehavior default_behaviors()
5342
template<typename ContainerType, typename HeaderType = Vector<StringView>>
5443
class XSV {
5544
public:
56-
XSV(OutputStream& output, ContainerType const& data, WriterTraits traits, HeaderType const& headers = {}, WriterBehavior behaviors = default_behaviors())
45+
XSV(Core::Stream::Handle<Core::Stream::Stream> output, ContainerType const& data, WriterTraits traits, HeaderType headers = {}, WriterBehavior behaviors = default_behaviors())
5746
: m_data(data)
5847
, m_traits(move(traits))
5948
, m_behaviors(behaviors)
6049
, m_names(headers)
61-
, m_output(output)
50+
, m_output(move(output))
6251
{
6352
if (!headers.is_empty())
6453
m_behaviors = m_behaviors | WriterBehavior::WriteHeaders;
6554
}
6655

6756
virtual ~XSV() = default;
6857

69-
bool has_error() const { return m_error != WriteError::None; }
70-
WriteError error() const { return m_error; }
71-
DeprecatedString error_string() const
72-
{
73-
switch (m_error) {
74-
#define E(x, y) \
75-
case WriteError::x: \
76-
return y;
77-
78-
ENUMERATE_WRITE_ERRORS();
79-
#undef E
80-
}
81-
VERIFY_NOT_REACHED();
82-
}
83-
84-
void generate()
58+
ErrorOr<void> generate()
8559
{
8660
auto with_headers = has_flag(m_behaviors, WriterBehavior::WriteHeaders);
8761
if (with_headers) {
88-
write_row(m_names);
89-
if (m_output.write({ "\n", 1 }) != 1)
90-
set_error(WriteError::InternalError);
62+
TRY(write_row(m_names));
63+
TRY(m_output->write_entire_buffer({ "\n", 1 }));
9164
}
9265

9366
for (auto&& row : m_data) {
9467
if (with_headers) {
9568
if (row.size() != m_names.size())
96-
set_error(WriteError::NonConformingColumnCount);
69+
return Error::from_string_literal("Header count does not match given column count");
9770
}
9871

99-
write_row(row);
100-
if (m_output.write({ "\n", 1 }) != 1)
101-
set_error(WriteError::InternalError);
72+
TRY(write_row(row));
73+
TRY(m_output->write_entire_buffer({ "\n", 1 }));
10274
}
75+
return {};
10376
}
10477

105-
void generate_preview()
78+
ErrorOr<void> generate_preview()
10679
{
10780
auto lines_written = 0;
10881
constexpr auto max_preview_lines = 8;
10982

11083
auto with_headers = has_flag(m_behaviors, WriterBehavior::WriteHeaders);
11184
if (with_headers) {
112-
write_row(m_names);
113-
if (m_output.write({ "\n", 1 }) != 1)
114-
set_error(WriteError::InternalError);
85+
TRY(write_row(m_names));
86+
TRY(m_output->write_entire_buffer({ "\n", 1 }));
11587
++lines_written;
11688
}
11789

11890
for (auto&& row : m_data) {
11991
if (with_headers) {
12092
if (row.size() != m_names.size())
121-
set_error(WriteError::NonConformingColumnCount);
93+
return Error::from_string_literal("Header count does not match given column count");
12294
}
12395

124-
write_row(row);
125-
if (m_output.write({ "\n", 1 }) != 1)
126-
set_error(WriteError::InternalError);
96+
TRY(write_row(row));
97+
TRY(m_output->write_entire_buffer({ "\n", 1 }));
12798
++lines_written;
12899

129100
if (lines_written >= max_preview_lines)
130101
break;
131102
}
103+
return {};
132104
}
133105

134106
private:
135-
void set_error(WriteError error)
136-
{
137-
if (m_error == WriteError::None)
138-
m_error = error;
139-
}
140-
141107
template<typename T>
142-
void write_row(T&& row)
108+
ErrorOr<void> write_row(T&& row)
143109
{
144110
bool first = true;
145111
for (auto&& entry : row) {
146112
if (!first) {
147-
if (m_output.write(m_traits.separator.bytes()) != m_traits.separator.length())
148-
set_error(WriteError::InternalError);
113+
TRY(m_output->write_entire_buffer(m_traits.separator.bytes()));
149114
}
150115
first = false;
151-
write_entry(entry);
116+
TRY(write_entry(entry));
152117
}
118+
return {};
153119
}
154120

155121
template<typename T>
156-
void write_entry(T&& entry)
122+
ErrorOr<void> write_entry(T&& entry)
157123
{
158124
auto string = DeprecatedString::formatted("{}", FormatIfSupported(entry));
159125

@@ -169,49 +135,42 @@ class XSV {
169135
}
170136

171137
if (safe_to_write_normally) {
172-
if (m_output.write(string.bytes()) != string.length())
173-
set_error(WriteError::InternalError);
174-
return;
138+
if (!string.is_empty())
139+
TRY(m_output->write_entire_buffer(string.bytes()));
140+
return {};
175141
}
176142

177-
if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length())
178-
set_error(WriteError::InternalError);
143+
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
179144

180145
GenericLexer lexer(string);
181146
while (!lexer.is_eof()) {
182147
if (lexer.consume_specific(m_traits.quote)) {
183148
switch (m_traits.quote_escape) {
184149
case WriterTraits::Repeat:
185-
if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length())
186-
set_error(WriteError::InternalError);
187-
if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length())
188-
set_error(WriteError::InternalError);
150+
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
151+
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
189152
break;
190153
case WriterTraits::Backslash:
191-
if (m_output.write({ "\\", 1 }) != 1)
192-
set_error(WriteError::InternalError);
193-
if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length())
194-
set_error(WriteError::InternalError);
154+
TRY(m_output->write_entire_buffer({ "\\", 1 }));
155+
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
195156
break;
196157
}
197158
continue;
198159
}
199160

200161
auto ch = lexer.consume();
201-
if (m_output.write({ &ch, 1 }) != 1)
202-
set_error(WriteError::InternalError);
162+
TRY(m_output->write_entire_buffer({ &ch, 1 }));
203163
}
204164

205-
if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length())
206-
set_error(WriteError::InternalError);
165+
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
166+
return {};
207167
}
208168

209169
ContainerType const& m_data;
210170
WriterTraits m_traits;
211171
WriterBehavior m_behaviors;
212-
HeaderType const& m_names;
213-
WriteError m_error { WriteError::None };
214-
OutputStream& m_output;
172+
HeaderType m_names;
173+
Core::Stream::Handle<Core::Stream::Stream> m_output;
215174
};
216175

217176
}

0 commit comments

Comments
 (0)