Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ The header file `src/lgx.h` is installed to `include/` when using `make install`
- `lgx_set_version(pkg, version) → lgx_result_t` - Set package version
- `lgx_get_description(pkg) → const char*` - Get package description (owned by library)
- `lgx_set_description(pkg, description)` - Set package description
- `lgx_get_icon(pkg) → const char*` - Get package icon path (owned by library)
- `lgx_set_icon(pkg, icon)` - Set package icon path
- `lgx_get_manifest_json(pkg) → const char*` - Get the full manifest as a JSON string (owned by library)

**Memory Management:**
Expand Down
3 changes: 3 additions & 0 deletions docs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ The manifest is a UTF-8 encoded JSON file with the following required fields:
"author": "Author Name",
"type": "library",
"category": "crypto",
"icon": "icon.png",
"dependencies": ["dep1", "dep2"],
"main": {
"linux-amd64": "path/to/main.so",
Expand All @@ -82,6 +83,7 @@ The manifest is a UTF-8 encoded JSON file with the following required fields:
| `author` | string | Author/maintainer name | Human metadata |
| `type` | string | Package type classification | Classification |
| `category` | string | Package category | Classification |
| `icon` | string | Relative path to icon file bundled in the package | Display/branding |
| `dependencies` | array | List of dependency strings | Runtime needs |
| `main` | object | Map of variant name → relative path to entry point (e.g ) `"linux-amd64": "path/to/main.so"` means `linux-amd64/path/to/main.so` | Entry point resolution |

Expand Down Expand Up @@ -156,6 +158,7 @@ lgx create <name>
- `author`: `""`
- `type`: `""`
- `category`: `""`
- `icon`: `""`
- `dependencies`: `[]`
- `main`: `{}`
4. Create empty `variants/` directory
Expand Down
10 changes: 9 additions & 1 deletion src/core/manifest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Manifest::Manifest()
, author("")
, type("")
, category("")
, icon("")
{
}

Expand Down Expand Up @@ -70,7 +71,13 @@ std::optional<Manifest> Manifest::fromJson(const std::string& jsonStr) {
return std::nullopt;
}
m.category = j["category"].get<std::string>();


if (!j.contains("icon") || !j["icon"].is_string()) {
lastError_ = "Missing or invalid 'icon' field";
return std::nullopt;
}
m.icon = j["icon"].get<std::string>();

if (!j.contains("dependencies") || !j["dependencies"].is_array()) {
lastError_ = "Missing or invalid 'dependencies' field";
return std::nullopt;
Expand Down Expand Up @@ -115,6 +122,7 @@ std::string Manifest::toJson() const {
j["author"] = author;
j["type"] = type;
j["category"] = category;
j["icon"] = icon;
j["dependencies"] = dependencies;

// main as object with sorted keys
Expand Down
1 change: 1 addition & 0 deletions src/core/manifest.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class Manifest {
std::string author;
std::string type;
std::string category;
std::string icon;
std::vector<std::string> dependencies;

// Main mapping: variant -> relative path to entry point
Expand Down
1 change: 1 addition & 0 deletions src/core/package.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Package::Result Package::create(
pkg.manifest_.author = "";
pkg.manifest_.type = "";
pkg.manifest_.category = "";
pkg.manifest_.icon = "";
pkg.manifest_.dependencies = {};

// Create skeleton with manifest and variants directory
Expand Down
16 changes: 16 additions & 0 deletions src/lgx.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,22 @@ LGX_EXPORT const char* lgx_get_description(lgx_package_t pkg);
*/
LGX_EXPORT void lgx_set_description(lgx_package_t pkg, const char* description);

/**
* Get the package icon path from manifest.
*
* @param pkg Package handle
* @return Package icon path, owned by library (valid until package is freed)
*/
LGX_EXPORT const char* lgx_get_icon(lgx_package_t pkg);

/**
* Set the package icon path in manifest.
*
* @param pkg Package handle
* @param icon New icon path string
*/
LGX_EXPORT void lgx_set_icon(lgx_package_t pkg, const char* icon);

/**
* Get the full manifest as a JSON string.
*
Expand Down
22 changes: 22 additions & 0 deletions src/lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ struct lgx_package_opaque {
std::string name_cache;
std::string version_cache;
std::string description_cache;
std::string icon_cache;
std::string manifest_json_cache;
};

Expand Down Expand Up @@ -280,6 +281,27 @@ LGX_EXPORT void lgx_set_description(lgx_package_t pkg, const char* description)
pkg->pkg->getManifest().description = description;
}

LGX_EXPORT const char* lgx_get_icon(lgx_package_t pkg) {
if (!pkg) {
set_error("Invalid argument: pkg cannot be NULL");
return nullptr;
}

clear_error();
pkg->icon_cache = pkg->pkg->getManifest().icon;
return pkg->icon_cache.c_str();
}

LGX_EXPORT void lgx_set_icon(lgx_package_t pkg, const char* icon) {
if (!pkg || !icon) {
set_error("Invalid arguments: pkg and icon cannot be NULL");
return;
}

clear_error();
pkg->pkg->getManifest().icon = icon;
}

LGX_EXPORT const char* lgx_get_manifest_json(lgx_package_t pkg) {
if (!pkg) {
set_error("Invalid argument: pkg cannot be NULL");
Expand Down
17 changes: 14 additions & 3 deletions tests/test_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,12 @@ TEST_F(LibraryTest, GetPackageMetadata) {
// Get description (should be empty initially)
const char* desc = lgx_get_description(pkg);
ASSERT_NE(desc, nullptr);


// Get icon (should be empty initially)
const char* icon = lgx_get_icon(pkg);
ASSERT_NE(icon, nullptr);
EXPECT_STREQ(icon, "");

lgx_free_package(pkg);
}

Expand All @@ -123,7 +128,12 @@ TEST_F(LibraryTest, SetPackageMetadata) {
lgx_set_description(pkg, "Test package");
const char* desc = lgx_get_description(pkg);
EXPECT_STREQ(desc, "Test package");


// Set icon
lgx_set_icon(pkg, "icon.png");
const char* icon = lgx_get_icon(pkg);
EXPECT_STREQ(icon, "icon.png");

lgx_free_package(pkg);
}

Expand Down Expand Up @@ -303,7 +313,8 @@ TEST_F(LibraryTest, NullPackageHandles) {
EXPECT_EQ(lgx_get_name(nullptr), nullptr);
EXPECT_EQ(lgx_get_version(nullptr), nullptr);
EXPECT_EQ(lgx_get_description(nullptr), nullptr);

EXPECT_EQ(lgx_get_icon(nullptr), nullptr);

// These should not crash with NULL
lgx_free_package(nullptr);
}
Expand Down
24 changes: 24 additions & 0 deletions tests/test_manifest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ static const char* VALID_MANIFEST_JSON = R"({
"author": "Test Author",
"type": "library",
"category": "test",
"icon": "icon.png",
"dependencies": ["dep1", "dep2"],
"main": {
"linux-amd64": "lib/test.so",
Expand All @@ -34,6 +35,7 @@ TEST(ManifestTest, FromJson_ValidManifest) {
EXPECT_EQ(manifest->author, "Test Author");
EXPECT_EQ(manifest->type, "library");
EXPECT_EQ(manifest->category, "test");
EXPECT_EQ(manifest->icon, "icon.png");
EXPECT_EQ(manifest->dependencies.size(), 2);
EXPECT_EQ(manifest->main.size(), 2);
}
Expand All @@ -46,6 +48,7 @@ TEST(ManifestTest, FromJson_MissingManifestVersion) {
"author": "",
"type": "",
"category": "",
"icon": "",
"dependencies": [],
"main": {}
})";
Expand All @@ -62,6 +65,7 @@ TEST(ManifestTest, FromJson_MissingName) {
"author": "",
"type": "",
"category": "",
"icon": "",
"dependencies": [],
"main": {}
})";
Expand All @@ -79,6 +83,7 @@ TEST(ManifestTest, FromJson_MissingMain) {
"author": "",
"type": "",
"category": "",
"icon": "",
"dependencies": []
})";

Expand All @@ -94,6 +99,23 @@ TEST(ManifestTest, FromJson_InvalidJson) {
EXPECT_FALSE(Manifest::getLastError().empty());
}

TEST(ManifestTest, FromJson_MissingIcon) {
const char* json = R"({
"manifestVersion": "0.1.0",
"name": "test",
"version": "1.0.0",
"description": "",
"author": "",
"type": "",
"category": "",
"dependencies": [],
"main": {}
})";

auto manifest = Manifest::fromJson(json);
EXPECT_FALSE(manifest.has_value());
}

TEST(ManifestTest, FromJson_EmptyDependencies) {
const char* json = R"({
"manifestVersion": "0.1.0",
Expand All @@ -103,6 +125,7 @@ TEST(ManifestTest, FromJson_EmptyDependencies) {
"author": "",
"type": "",
"category": "",
"icon": "",
"dependencies": [],
"main": {}
})";
Expand All @@ -127,6 +150,7 @@ TEST(ManifestTest, ToJson_Roundtrip) {
EXPECT_EQ(parsed->manifestVersion, original->manifestVersion);
EXPECT_EQ(parsed->name, original->name);
EXPECT_EQ(parsed->version, original->version);
EXPECT_EQ(parsed->icon, original->icon);
EXPECT_EQ(parsed->main, original->main);
}

Expand Down
Loading