mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 18:17:45 +00:00
Spreadsheet: Port XSV writer to Core::Stream
This commit is contained in:
parent
5793f7749c
commit
3a8450ae11
5 changed files with 74 additions and 120 deletions
|
@ -9,10 +9,10 @@
|
|||
#include <AK/DeprecatedString.h>
|
||||
#include <AK/GenericLexer.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Stream.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/Stream.h>
|
||||
|
||||
namespace Writer {
|
||||
|
||||
|
@ -34,17 +34,6 @@ struct WriterTraits {
|
|||
} quote_escape { Repeat };
|
||||
};
|
||||
|
||||
#define ENUMERATE_WRITE_ERRORS() \
|
||||
E(None, "No errors") \
|
||||
E(NonConformingColumnCount, "Header count does not match given column count") \
|
||||
E(InternalError, "Internal error")
|
||||
|
||||
enum class WriteError {
|
||||
#define E(name, _) name,
|
||||
ENUMERATE_WRITE_ERRORS()
|
||||
#undef E
|
||||
};
|
||||
|
||||
constexpr WriterBehavior default_behaviors()
|
||||
{
|
||||
return WriterBehavior::None;
|
||||
|
@ -53,12 +42,12 @@ constexpr WriterBehavior default_behaviors()
|
|||
template<typename ContainerType, typename HeaderType = Vector<StringView>>
|
||||
class XSV {
|
||||
public:
|
||||
XSV(OutputStream& output, ContainerType const& data, WriterTraits traits, HeaderType const& headers = {}, WriterBehavior behaviors = default_behaviors())
|
||||
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(output)
|
||||
, m_output(move(output))
|
||||
{
|
||||
if (!headers.is_empty())
|
||||
m_behaviors = m_behaviors | WriterBehavior::WriteHeaders;
|
||||
|
@ -66,94 +55,71 @@ public:
|
|||
|
||||
virtual ~XSV() = default;
|
||||
|
||||
bool has_error() const { return m_error != WriteError::None; }
|
||||
WriteError error() const { return m_error; }
|
||||
DeprecatedString error_string() const
|
||||
{
|
||||
switch (m_error) {
|
||||
#define E(x, y) \
|
||||
case WriteError::x: \
|
||||
return y;
|
||||
|
||||
ENUMERATE_WRITE_ERRORS();
|
||||
#undef E
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
void generate()
|
||||
ErrorOr<void> generate()
|
||||
{
|
||||
auto with_headers = has_flag(m_behaviors, WriterBehavior::WriteHeaders);
|
||||
if (with_headers) {
|
||||
write_row(m_names);
|
||||
if (m_output.write({ "\n", 1 }) != 1)
|
||||
set_error(WriteError::InternalError);
|
||||
TRY(write_row(m_names));
|
||||
TRY(m_output->write_entire_buffer({ "\n", 1 }));
|
||||
}
|
||||
|
||||
for (auto&& row : m_data) {
|
||||
if (with_headers) {
|
||||
if (row.size() != m_names.size())
|
||||
set_error(WriteError::NonConformingColumnCount);
|
||||
return Error::from_string_literal("Header count does not match given column count");
|
||||
}
|
||||
|
||||
write_row(row);
|
||||
if (m_output.write({ "\n", 1 }) != 1)
|
||||
set_error(WriteError::InternalError);
|
||||
TRY(write_row(row));
|
||||
TRY(m_output->write_entire_buffer({ "\n", 1 }));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void generate_preview()
|
||||
ErrorOr<void> generate_preview()
|
||||
{
|
||||
auto lines_written = 0;
|
||||
constexpr auto max_preview_lines = 8;
|
||||
|
||||
auto with_headers = has_flag(m_behaviors, WriterBehavior::WriteHeaders);
|
||||
if (with_headers) {
|
||||
write_row(m_names);
|
||||
if (m_output.write({ "\n", 1 }) != 1)
|
||||
set_error(WriteError::InternalError);
|
||||
TRY(write_row(m_names));
|
||||
TRY(m_output->write_entire_buffer({ "\n", 1 }));
|
||||
++lines_written;
|
||||
}
|
||||
|
||||
for (auto&& row : m_data) {
|
||||
if (with_headers) {
|
||||
if (row.size() != m_names.size())
|
||||
set_error(WriteError::NonConformingColumnCount);
|
||||
return Error::from_string_literal("Header count does not match given column count");
|
||||
}
|
||||
|
||||
write_row(row);
|
||||
if (m_output.write({ "\n", 1 }) != 1)
|
||||
set_error(WriteError::InternalError);
|
||||
TRY(write_row(row));
|
||||
TRY(m_output->write_entire_buffer({ "\n", 1 }));
|
||||
++lines_written;
|
||||
|
||||
if (lines_written >= max_preview_lines)
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
void set_error(WriteError error)
|
||||
{
|
||||
if (m_error == WriteError::None)
|
||||
m_error = error;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write_row(T&& row)
|
||||
ErrorOr<void> write_row(T&& row)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto&& entry : row) {
|
||||
if (!first) {
|
||||
if (m_output.write(m_traits.separator.bytes()) != m_traits.separator.length())
|
||||
set_error(WriteError::InternalError);
|
||||
TRY(m_output->write_entire_buffer(m_traits.separator.bytes()));
|
||||
}
|
||||
first = false;
|
||||
write_entry(entry);
|
||||
TRY(write_entry(entry));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write_entry(T&& entry)
|
||||
ErrorOr<void> write_entry(T&& entry)
|
||||
{
|
||||
auto string = DeprecatedString::formatted("{}", FormatIfSupported(entry));
|
||||
|
||||
|
@ -169,49 +135,42 @@ private:
|
|||
}
|
||||
|
||||
if (safe_to_write_normally) {
|
||||
if (m_output.write(string.bytes()) != string.length())
|
||||
set_error(WriteError::InternalError);
|
||||
return;
|
||||
if (!string.is_empty())
|
||||
TRY(m_output->write_entire_buffer(string.bytes()));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length())
|
||||
set_error(WriteError::InternalError);
|
||||
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:
|
||||
if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length())
|
||||
set_error(WriteError::InternalError);
|
||||
if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length())
|
||||
set_error(WriteError::InternalError);
|
||||
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
|
||||
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
|
||||
break;
|
||||
case WriterTraits::Backslash:
|
||||
if (m_output.write({ "\\", 1 }) != 1)
|
||||
set_error(WriteError::InternalError);
|
||||
if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length())
|
||||
set_error(WriteError::InternalError);
|
||||
TRY(m_output->write_entire_buffer({ "\\", 1 }));
|
||||
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ch = lexer.consume();
|
||||
if (m_output.write({ &ch, 1 }) != 1)
|
||||
set_error(WriteError::InternalError);
|
||||
TRY(m_output->write_entire_buffer({ &ch, 1 }));
|
||||
}
|
||||
|
||||
if (m_output.write(m_traits.quote.bytes()) != m_traits.quote.length())
|
||||
set_error(WriteError::InternalError);
|
||||
TRY(m_output->write_entire_buffer(m_traits.quote.bytes()));
|
||||
return {};
|
||||
}
|
||||
|
||||
ContainerType const& m_data;
|
||||
WriterTraits m_traits;
|
||||
WriterBehavior m_behaviors;
|
||||
HeaderType const& m_names;
|
||||
WriteError m_error { WriteError::None };
|
||||
OutputStream& m_output;
|
||||
HeaderType m_names;
|
||||
Core::Stream::Handle<Core::Stream::Stream> m_output;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue