mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-08 20:32:56 +03:00
Spreadsheet: Move to a non-owning model for Stream
in Writer::XSV
This commit is contained in:
parent
fc413711ee
commit
4952cdfe2b
Notes:
sideshowbarker
2024-07-17 23:07:41 +09:00
Author: https://github.com/LucasChollet Commit: https://github.com/SerenityOS/serenity/commit/4952cdfe2b Pull-request: https://github.com/SerenityOS/serenity/pull/17018 Reviewed-by: https://github.com/ADKaster
@ -88,7 +88,7 @@ CSVExportDialogPage::CSVExportDialogPage(Sheet const& sheet)
|
||||
update_preview();
|
||||
}
|
||||
|
||||
auto CSVExportDialogPage::make_writer(Core::Stream::Handle<Core::Stream::Stream> stream) -> ErrorOr<NonnullOwnPtr<XSV>>
|
||||
auto CSVExportDialogPage::generate(Core::Stream::Stream& stream, GenerationType type) -> ErrorOr<void>
|
||||
{
|
||||
auto delimiter = TRY([this]() -> ErrorOr<DeprecatedString> {
|
||||
if (m_delimiter_other_radio->is_checked()) {
|
||||
@ -150,15 +150,25 @@ auto CSVExportDialogPage::make_writer(Core::Stream::Handle<Core::Stream::Stream>
|
||||
if (should_quote_all_fields)
|
||||
behaviors = behaviors | Writer::WriterBehavior::QuoteAll;
|
||||
|
||||
return try_make<XSV>(move(stream), m_data, move(traits), *headers, behaviors);
|
||||
switch (type) {
|
||||
case GenerationType::Normal:
|
||||
TRY((Writer::XSV<decltype(m_data), Vector<DeprecatedString>>::generate(stream, m_data, move(traits), *headers, behaviors)));
|
||||
break;
|
||||
case GenerationType::Preview:
|
||||
TRY((Writer::XSV<decltype(m_data), decltype(*headers)>::generate_preview(stream, m_data, move(traits), *headers, behaviors)));
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void CSVExportDialogPage::update_preview()
|
||||
{
|
||||
auto maybe_error = [this]() -> ErrorOr<void> {
|
||||
Core::Stream::AllocatingMemoryStream memory_stream;
|
||||
auto writer = TRY(make_writer(Core::Stream::Handle<Core::Stream::Stream>(memory_stream)));
|
||||
TRY(writer->generate_preview());
|
||||
TRY(generate(memory_stream, GenerationType::Preview));
|
||||
auto buffer = TRY(memory_stream.read_until_eof());
|
||||
m_data_preview_text_editor->set_text(StringView(buffer));
|
||||
m_data_preview_text_editor->update();
|
||||
@ -185,8 +195,8 @@ ErrorOr<void> ExportDialog::make_and_run_for(StringView mime, NonnullOwnPtr<Core
|
||||
if (wizard->exec() != GUI::Dialog::ExecResult::OK)
|
||||
return Error::from_string_literal("CSV Export was cancelled");
|
||||
|
||||
auto writer = TRY(page.make_writer(move(file)));
|
||||
return writer->generate();
|
||||
TRY(page.generate(*file, CSVExportDialogPage::GenerationType::Normal));
|
||||
return {};
|
||||
};
|
||||
|
||||
auto export_worksheet = [&]() -> ErrorOr<void> {
|
||||
|
@ -18,12 +18,16 @@ class Sheet;
|
||||
class Workbook;
|
||||
|
||||
struct CSVExportDialogPage {
|
||||
using XSV = Writer::XSV<Vector<Vector<DeprecatedString>>, Vector<DeprecatedString>>;
|
||||
|
||||
explicit CSVExportDialogPage(Sheet const&);
|
||||
|
||||
NonnullRefPtr<GUI::WizardPage> page() { return *m_page; }
|
||||
ErrorOr<NonnullOwnPtr<XSV>> make_writer(Core::Stream::Handle<Core::Stream::Stream>);
|
||||
|
||||
enum class GenerationType {
|
||||
Normal,
|
||||
Preview
|
||||
};
|
||||
|
||||
ErrorOr<void> generate(Core::Stream::Stream&, GenerationType);
|
||||
|
||||
protected:
|
||||
void update_preview();
|
||||
|
@ -12,12 +12,18 @@
|
||||
|
||||
namespace Writer {
|
||||
|
||||
template<typename ContainerType>
|
||||
class CSV : public XSV<ContainerType> {
|
||||
class CSV {
|
||||
public:
|
||||
CSV(Core::Stream::Handle<Core::Stream::Stream> output, ContainerType const& data, Vector<StringView> headers = {}, WriterBehavior behaviors = default_behaviors())
|
||||
: XSV<ContainerType>(move(output), data, { ",", "\"", WriterTraits::Repeat }, move(headers), behaviors)
|
||||
template<typename ContainerType>
|
||||
static ErrorOr<void> generate(Core::Stream::Stream& output, ContainerType const& data, Vector<StringView> headers = {}, WriterBehavior behaviors = default_behaviors())
|
||||
{
|
||||
return XSV<ContainerType>::generate(output, data, { ",", "\"", WriterTraits::Repeat }, move(headers), behaviors);
|
||||
}
|
||||
|
||||
template<typename ContainerType>
|
||||
static ErrorOr<void> generate_preview(Core::Stream::Stream& output, ContainerType const& data, Vector<StringView> headers = {}, WriterBehavior behaviors = default_behaviors())
|
||||
{
|
||||
return XSV<ContainerType>::generate_preview(output, data, { ",", "\"", WriterTraits::Repeat }, move(headers), behaviors);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include "../CSV.h"
|
||||
#include "../XSV.h"
|
||||
#include <LibCore/MemoryStream.h>
|
||||
|
||||
TEST_CASE(can_write)
|
||||
@ -19,8 +18,7 @@ TEST_CASE(can_write)
|
||||
};
|
||||
|
||||
Core::Stream::AllocatingMemoryStream stream;
|
||||
auto csv = Writer::CSV(Core::Stream::Handle<Core::Stream::Stream>(stream), data);
|
||||
MUST(csv.generate());
|
||||
MUST(Writer::CSV::generate(stream, data));
|
||||
|
||||
auto expected_output = R"~(1,2,3
|
||||
4,5,6
|
||||
@ -40,8 +38,7 @@ TEST_CASE(can_write_with_header)
|
||||
};
|
||||
|
||||
Core::Stream::AllocatingMemoryStream stream;
|
||||
auto csv = Writer::CSV(Core::Stream::Handle<Core::Stream::Stream>(stream), data, { "A"sv, "B\""sv, "C"sv });
|
||||
MUST(csv.generate());
|
||||
MUST(Writer::CSV::generate(stream, data, { "A"sv, "B\""sv, "C"sv }));
|
||||
|
||||
auto expected_output = R"~(A,"B""",C
|
||||
1,2,3
|
||||
@ -61,8 +58,7 @@ TEST_CASE(can_write_with_different_behaviors)
|
||||
};
|
||||
|
||||
Core::Stream::AllocatingMemoryStream stream;
|
||||
auto csv = Writer::CSV(Core::Stream::Handle<Core::Stream::Stream>(stream), data, { "A"sv, "B\""sv, "C"sv }, Writer::WriterBehavior::QuoteOnlyInFieldStart | Writer::WriterBehavior::WriteHeaders);
|
||||
MUST(csv.generate());
|
||||
MUST(Writer::CSV::generate(stream, data, { "A"sv, "B\""sv, "C"sv }, Writer::WriterBehavior::QuoteOnlyInFieldStart | Writer::WriterBehavior::WriteHeaders));
|
||||
|
||||
auto expected_output = R"~(A,B",C
|
||||
Well,Hello",Friends
|
||||
|
@ -42,59 +42,48 @@ constexpr WriterBehavior default_behaviors()
|
||||
template<typename ContainerType, typename HeaderType = Vector<StringView>>
|
||||
class XSV {
|
||||
public:
|
||||
XSV(Core::Stream::Handle<Core::Stream::Stream> output, ContainerType const& data, WriterTraits traits, HeaderType headers = {}, WriterBehavior behaviors = default_behaviors())
|
||||
: m_data(data)
|
||||
, m_traits(move(traits))
|
||||
, m_behaviors(behaviors)
|
||||
, m_names(headers)
|
||||
, m_output(move(output))
|
||||
static ErrorOr<void> generate(Core::Stream::Stream& output, ContainerType const& data, WriterTraits traits, HeaderType headers = {}, WriterBehavior behaviors = default_behaviors())
|
||||
{
|
||||
if (!headers.is_empty())
|
||||
m_behaviors = m_behaviors | WriterBehavior::WriteHeaders;
|
||||
}
|
||||
|
||||
virtual ~XSV() = default;
|
||||
|
||||
ErrorOr<void> generate()
|
||||
{
|
||||
auto with_headers = has_flag(m_behaviors, WriterBehavior::WriteHeaders);
|
||||
auto writer = XSV(output, data, traits, headers, behaviors);
|
||||
auto with_headers = has_flag(writer.m_behaviors, WriterBehavior::WriteHeaders);
|
||||
if (with_headers) {
|
||||
TRY(write_row(m_names));
|
||||
TRY(m_output->write_entire_buffer({ "\n", 1 }));
|
||||
TRY(writer.write_row(writer.m_names));
|
||||
TRY(writer.m_output.write_entire_buffer({ "\n", 1 }));
|
||||
}
|
||||
|
||||
for (auto&& row : m_data) {
|
||||
for (auto&& row : writer.m_data) {
|
||||
if (with_headers) {
|
||||
if (row.size() != m_names.size())
|
||||
if (row.size() != writer.m_names.size())
|
||||
return Error::from_string_literal("Header count does not match given column count");
|
||||
}
|
||||
|
||||
TRY(write_row(row));
|
||||
TRY(m_output->write_entire_buffer({ "\n", 1 }));
|
||||
TRY(writer.write_row(row));
|
||||
TRY(writer.m_output.write_entire_buffer({ "\n", 1 }));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> generate_preview()
|
||||
static ErrorOr<void> generate_preview(Core::Stream::Stream& output, ContainerType const& data, WriterTraits traits, HeaderType headers = {}, WriterBehavior behaviors = default_behaviors())
|
||||
{
|
||||
auto writer = XSV(output, data, traits, headers, behaviors);
|
||||
auto lines_written = 0;
|
||||
constexpr auto max_preview_lines = 8;
|
||||
|
||||
auto with_headers = has_flag(m_behaviors, WriterBehavior::WriteHeaders);
|
||||
auto with_headers = has_flag(writer.m_behaviors, WriterBehavior::WriteHeaders);
|
||||
if (with_headers) {
|
||||
TRY(write_row(m_names));
|
||||
TRY(m_output->write_entire_buffer({ "\n", 1 }));
|
||||
TRY(writer.write_row(writer.m_names));
|
||||
TRY(writer.m_output.write_entire_buffer({ "\n", 1 }));
|
||||
++lines_written;
|
||||
}
|
||||
|
||||
for (auto&& row : m_data) {
|
||||
for (auto&& row : writer.m_data) {
|
||||
if (with_headers) {
|
||||
if (row.size() != m_names.size())
|
||||
if (row.size() != writer.m_names.size())
|
||||
return Error::from_string_literal("Header count does not match given column count");
|
||||
}
|
||||
|
||||
TRY(write_row(row));
|
||||
TRY(m_output->write_entire_buffer({ "\n", 1 }));
|
||||
TRY(writer.write_row(row));
|
||||
TRY(writer.m_output.write_entire_buffer({ "\n", 1 }));
|
||||
++lines_written;
|
||||
|
||||
if (lines_written >= max_preview_lines)
|
||||
@ -104,13 +93,24 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
XSV(Core::Stream::Stream& output, ContainerType const& data, WriterTraits traits, HeaderType headers = {}, WriterBehavior behaviors = default_behaviors())
|
||||
: m_data(data)
|
||||
, m_traits(move(traits))
|
||||
, m_behaviors(behaviors)
|
||||
, m_names(headers)
|
||||
, m_output(output)
|
||||
{
|
||||
if (!headers.is_empty())
|
||||
m_behaviors = m_behaviors | WriterBehavior::WriteHeaders;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> write_row(T&& row)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto&& entry : row) {
|
||||
if (!first) {
|
||||
TRY(m_output->write_entire_buffer(m_traits.separator.bytes()));
|
||||
TRY(m_output.write_entire_buffer(m_traits.separator.bytes()));
|
||||
}
|
||||
first = false;
|
||||
TRY(write_entry(entry));
|
||||
@ -136,33 +136,33 @@ private:
|
||||
|
||||
if (safe_to_write_normally) {
|
||||
if (!string.is_empty())
|
||||
TRY(m_output->write_entire_buffer(string.bytes()));
|
||||
TRY(m_output.write_entire_buffer(string.bytes()));
|
||||
return {};
|
||||
}
|
||||
|
||||
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
|
||||
TRY(m_output.write_entire_buffer(m_traits.quote.bytes()));
|
||||
|
||||
GenericLexer lexer(string);
|
||||
while (!lexer.is_eof()) {
|
||||
if (lexer.consume_specific(m_traits.quote)) {
|
||||
switch (m_traits.quote_escape) {
|
||||
case WriterTraits::Repeat:
|
||||
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
|
||||
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
|
||||
TRY(m_output.write_entire_buffer(m_traits.quote.bytes()));
|
||||
TRY(m_output.write_entire_buffer(m_traits.quote.bytes()));
|
||||
break;
|
||||
case WriterTraits::Backslash:
|
||||
TRY(m_output->write_entire_buffer({ "\\", 1 }));
|
||||
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
|
||||
TRY(m_output.write_entire_buffer({ "\\", 1 }));
|
||||
TRY(m_output.write_entire_buffer(m_traits.quote.bytes()));
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ch = lexer.consume();
|
||||
TRY(m_output->write_entire_buffer({ &ch, 1 }));
|
||||
TRY(m_output.write_entire_buffer({ &ch, 1 }));
|
||||
}
|
||||
|
||||
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
|
||||
TRY(m_output.write_entire_buffer(m_traits.quote.bytes()));
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@ private:
|
||||
WriterTraits m_traits;
|
||||
WriterBehavior m_behaviors;
|
||||
HeaderType m_names;
|
||||
Core::Stream::Handle<Core::Stream::Stream> m_output;
|
||||
Core::Stream::Stream& m_output;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user