plan_execute/src/app.cpp

384 lines
11 KiB
C++
Raw Normal View History

2026-05-06 05:54:41 +00:00
#include "app.hpp"
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <stdexcept>
// ============================================================
// 匿名命名空间:简易 JSON 序列化 / 反序列化
// 仅支持当前 TodoItem 数据结构的 JSON 子集,无外部依赖。
// ============================================================
namespace {
/**
* @brief JSON
*/
static std::string json_escape(const std::string& s) {
std::string r;
r.reserve(s.size() + 4);
for (char c : s) {
switch (c) {
case '"': r += "\\\""; break;
case '\\': r += "\\\\"; break;
case '\n': r += "\\n"; break;
case '\t': r += "\\t"; break;
case '\r': r += "\\r"; break;
default: r += c; break;
}
}
return r;
}
/**
* @brief TodoItem next_id JSON
*/
static std::string items_to_json(const std::vector<TodoItem>& items, int next_id) {
std::ostringstream oss;
oss << "{\n \"items\": [\n";
for (size_t i = 0; i < items.size(); ++i) {
const auto& it = items[i];
oss << " {\n";
oss << " \"id\": " << it.id << ",\n";
oss << " \"title\": \"" << json_escape(it.title) << "\",\n";
oss << " \"description\": \"" << json_escape(it.description) << "\",\n";
oss << " \"completed\": " << (it.completed ? "true" : "false") << "\n";
oss << " }";
if (i + 1 < items.size()) oss << ",";
oss << "\n";
}
oss << " ],\n \"next_id\": " << next_id << "\n}\n";
return oss.str();
}
/**
* @brief JSON TodoItem
*
* JSON
*
*/
class JsonReader {
public:
explicit JsonReader(const std::string& input)
: s_(input), pos_(0) {}
/**
* @brief JSON items next_id
* @param[out] items
* @param[out] next_id ID
* @return true
*/
bool parse(std::vector<TodoItem>& items, int& next_id) {
items.clear();
skip_ws();
if (peek() != '{') return false;
advance(); // 跳过 '{'
while (pos_ < s_.size() && peek() != '}') {
skip_ws();
if (peek() == '}') break;
if (peek() == ',') { advance(); continue; }
std::string key = parse_string();
skip_ws();
if (peek() != ':') return false;
advance();
skip_ws();
if (key == "items") {
if (!parse_array(items)) return false;
} else if (key == "next_id") {
next_id = parse_int();
} else {
skip_value();
}
}
if (peek() == '}') advance();
return true;
}
private:
const std::string& s_;
size_t pos_ = 0;
/// @brief 查看当前字符,不移动指针。
char peek() const { return pos_ < s_.size() ? s_[pos_] : '\0'; }
/// @brief 读取当前字符并前进一位。
char advance() { return pos_ < s_.size() ? s_[pos_++] : '\0'; }
/// @brief 跳过空白字符。
void skip_ws() {
while (pos_ < s_.size() &&
std::isspace(static_cast<unsigned char>(s_[pos_]))) {
++pos_;
}
}
/// @brief 解析 JSON 字符串(含引号和转义处理)。
std::string parse_string() {
skip_ws();
if (peek() != '"') return {};
advance(); // 跳过开头的 "
std::string result;
while (pos_ < s_.size() && s_[pos_] != '"') {
if (s_[pos_] == '\\') {
advance();
if (pos_ < s_.size()) {
switch (s_[pos_]) {
case '"': result += '"'; break;
case '\\': result += '\\'; break;
case '/': result += '/'; break;
case 'n': result += '\n'; break;
case 't': result += '\t'; break;
case 'r': result += '\r'; break;
default: result += s_[pos_]; break;
}
}
} else {
result += s_[pos_];
}
++pos_;
}
if (peek() == '"') advance(); // 跳过结尾的 "
return result;
}
/// @brief 解析整数值(可选负号)。
int parse_int() {
skip_ws();
int sign = 1;
if (peek() == '-') { sign = -1; advance(); }
int val = 0;
while (pos_ < s_.size() &&
std::isdigit(static_cast<unsigned char>(s_[pos_]))) {
val = val * 10 + (s_[pos_] - '0');
++pos_;
}
return sign * val;
}
/**
* @brief true / false
* @param[out] value
* @return true
*/
bool parse_bool(bool& value) {
skip_ws();
if (s_.substr(pos_, 4) == "true") {
pos_ += 4;
value = true;
return true;
}
if (s_.substr(pos_, 5) == "false") {
pos_ += 5;
value = false;
return true;
}
return false;
}
/**
* @brief JSON TodoItem
*/
bool parse_array(std::vector<TodoItem>& items) {
skip_ws();
if (peek() != '[') return false;
advance(); // 跳过 '['
while (pos_ < s_.size() && peek() != ']') {
skip_ws();
if (peek() == ']') break;
if (peek() == ',') { advance(); continue; }
TodoItem item;
if (!parse_object(item)) return false;
items.push_back(std::move(item));
}
if (peek() == ']') advance();
return true;
}
/**
* @brief TodoItem JSON
*/
bool parse_object(TodoItem& item) {
skip_ws();
if (peek() != '{') return false;
advance(); // 跳过 '{'
while (pos_ < s_.size() && peek() != '}') {
skip_ws();
if (peek() == '}') break;
if (peek() == ',') { advance(); continue; }
std::string key = parse_string();
skip_ws();
if (peek() != ':') return false;
advance();
skip_ws();
if (key == "id") {
item.id = parse_int();
} else if (key == "title") {
item.title = parse_string();
} else if (key == "description") {
item.description = parse_string();
} else if (key == "completed") {
bool val = false;
if (parse_bool(val)) item.completed = val;
} else {
skip_value();
}
}
if (peek() == '}') advance();
return true;
}
/// @brief 跳过任意 JSON 值(用于忽略不关心的字段)。
void skip_value() {
skip_ws();
char c = peek();
if (c == '"') {
parse_string();
} else if (c == '{') {
int depth = 1;
advance();
while (pos_ < s_.size() && depth > 0) {
if (s_[pos_] == '{') ++depth;
else if (s_[pos_] == '}') --depth;
else if (s_[pos_] == '"') {
++pos_;
while (pos_ < s_.size() && s_[pos_] != '"') {
if (s_[pos_] == '\\') ++pos_;
++pos_;
}
}
++pos_;
}
} else if (c == '[') {
int depth = 1;
advance();
while (pos_ < s_.size() && depth > 0) {
if (s_[pos_] == '[') ++depth;
else if (s_[pos_] == ']') --depth;
else if (s_[pos_] == '"') {
++pos_;
while (pos_ < s_.size() && s_[pos_] != '"') {
if (s_[pos_] == '\\') ++pos_;
++pos_;
}
}
++pos_;
}
} else {
// 数字、布尔值、null 等
while (pos_ < s_.size() &&
!std::isspace(static_cast<unsigned char>(s_[pos_])) &&
s_[pos_] != ',' && s_[pos_] != '}' && s_[pos_] != ']') {
++pos_;
}
}
}
};
} // anonymous namespace
// ============================================================
// TodoService 实现
// ============================================================
TodoService::TodoService(const std::string& filepath)
: filepath_(filepath) {
load();
}
const std::vector<TodoItem>& TodoService::get_all() const {
return items_;
}
std::optional<TodoItem> TodoService::get_by_id(int id) const {
for (const auto& item : items_) {
if (item.id == id) return item;
}
return std::nullopt;
}
TodoItem TodoService::create(const std::string& title,
const std::string& description) {
TodoItem item;
item.id = next_id_++;
item.title = title;
item.description = description;
item.completed = false;
items_.push_back(item);
save();
return item;
}
std::optional<TodoItem> TodoService::update(
int id,
const std::optional<std::string>& title,
const std::optional<std::string>& description,
const std::optional<bool>& completed) {
for (auto& item : items_) {
if (item.id == id) {
if (title.has_value()) item.title = title.value();
if (description.has_value()) item.description = description.value();
if (completed.has_value()) item.completed = completed.value();
save();
return item;
}
}
return std::nullopt;
}
bool TodoService::delete_item(int id) {
auto it = std::find_if(items_.begin(), items_.end(),
[id](const TodoItem& item) { return item.id == id; });
if (it == items_.end()) return false;
items_.erase(it);
save();
return true;
}
void TodoService::load() {
std::ifstream ifs(filepath_);
if (!ifs.is_open()) {
items_.clear();
next_id_ = 1;
return;
}
std::stringstream buffer;
buffer << ifs.rdbuf();
std::string content = buffer.str();
if (content.empty()) {
items_.clear();
next_id_ = 1;
return;
}
items_.clear();
int parsed_next_id = 1;
JsonReader reader(content);
if (reader.parse(items_, parsed_next_id)) {
next_id_ = parsed_next_id;
// 确保 next_id 大于所有现有 ID避免冲突
for (const auto& item : items_) {
if (item.id >= next_id_) next_id_ = item.id + 1;
}
} else {
// 解析失败时回退到空状态
items_.clear();
next_id_ = 1;
}
}
void TodoService::save() {
std::ofstream ofs(filepath_);
if (!ofs.is_open()) return;
ofs << items_to_json(items_, next_id_);
}