sig_project.h

include/sig/project/sig_project.h

sig_project

typedef struct sig_project sig_project;

sig_project_load

sig_status_t sig_project_load(sig_persistence * store, const sig_capsule_registry * modules, sig_project ** out)
ParameterType
storesig_persistence *
modulesconst sig_capsule_registry *
outsig_project **

Loads every key of the "configurations" and then the "services" namespace of `store`, building the project's capsule configurations and services -- see sig_config_load_configuration()/sig_config_load_service() for the exact per-document schema (a trailing newline is fine). With the file backend these are <base>/configurations/<name>.cfg and <base>/services/<name>.cfg (see sig_persistence_file.h). `store` and `modules` are borrowed and must outlive the returned sig_project: the project saves and removes keys in `store` as it is edited. `modules` is created and owned by the caller, with every compiled-in sig_capsule registered before calling this. A namespace that does not exist (or that the store reports as SIG_ERR_NOT_FOUND) holds zero entries, not an error; a key that vanishes between listing and loading is skipped. Any other failure to list or load is SIG_ERR_PLATFORM (SIG_ERR_ALLOC for want of memory); a document that does not validate is SIG_ERR_PARSE (SIG_ERR_NOT_FOUND for an unregistered type, or an unknown configuration or receiver). Nothing is started by this call -- see sig_project_start_auto(). On any failure, everything built so far is torn down internally and *out is left untouched; no partially built project is ever returned.

sig_project_destroy

void sig_project_destroy(sig_project * project)
ParameterType
projectsig_project *

Stops every service still running, then destroys every service's instances, then every service, then every capsule configuration. Does not touch `modules` -- that stays the caller's.

sig_project_get_service

sig_status_t sig_project_get_service(const sig_project * project, const char * name, sig_service ** out)
ParameterType
projectconst sig_project *
nameconst char *
outsig_service **

sig_project_start_auto

sig_status_t sig_project_start_auto(sig_project * project)
ParameterType
projectsig_project *

Calls sig_service_start on every service whose definition had "auto_start": true. Best-effort across services, mirroring the convention sig_capsule_instance_notify() already established for multi-target operations: a failure starting one service does not stop the others from being attempted; returns SIG_OK only if every flagged service started successfully, otherwise the status of the last one that failed.

sig_project_stop_all

sig_status_t sig_project_stop_all(sig_project * project)
ParameterType
projectsig_project *

Calls sig_service_stop on every service in the project, regardless of how (or whether) it was started -- sig_service_stop is always a safe no-op on a service that was never started. Same best-effort convention as sig_project_start_auto.

sig_project_service_fn

typedef sig_status_t (*sig_project_service_fn)(const char *name, sig_service *service, void *ctx);

Read-only introspection, safe from any thread once the project is loaded (the service table is only mutated during sig_project_load() and sig_project_destroy()). The sig_service handed to the callback stays valid until sig_project_destroy(). Order is unspecified. A non-SIG_OK return from fn stops the walk and becomes its result.

sig_project_service_count

uint32_t sig_project_service_count(const sig_project * project)
ParameterType
projectconst sig_project *

sig_project_walk_services

sig_status_t sig_project_walk_services(const sig_project * project, sig_project_service_fn fn, void * ctx)
ParameterType
projectconst sig_project *
fnsig_project_service_fn
ctxvoid *
---- Editing ------------------------------------------------------- The project keeps the JSON definition of every configuration and service. These functions change definitions and persist them to the project's store under the key <name> in the "configurations" or "services" namespace, saved as the document followed by one newline (the file backend writes each key atomically). A document is validated exactly as sig_project_load() would; on any failure nothing is saved and nothing changes. A running service is never disturbed: its instances keep borrowing the configurations they were built from (a replaced configuration is retired, not freed, until sig_project_destroy). See sig_project_service_dirty(). Names must match [A-Za-z0-9_-]{1,64}: SIG_ERR_INVALID_ARG otherwise. An invalid document -- bad JSON, wrong shape, unregistered type, unknown configuration or receiver -- is SIG_ERR_PARSE. A failed store operation is SIG_ERR_PLATFORM (SIG_ERR_ALLOC for want of memory); removing a key the store no longer has is not a failure. Not thread-safe on their own: callers serialize them with each other and with service start/stop (sig_runtime_edit() does this).

sig_project_configuration_fn

typedef sig_status_t (*sig_project_configuration_fn)(const char *name,
                                                     const sig_capsule_configuration *configuration,
                                                     void *ctx);

sig_project_name_fn

typedef sig_status_t (*sig_project_name_fn)(const char *name, void *ctx);

sig_project_last_error

const char * sig_project_last_error(const sig_project * project)
ParameterType
projectconst sig_project *

Why the most recent put/patch was rejected, for a caller to show: a property message from the capsule's table ("end: required", "step: must be >= 1"), or "" when the failure has no more specific reason than the status. Cleared at the start of every put/patch; valid until the next edit. Like the edit functions it is serialized by sig_runtime_edit(). Never NULL.

sig_project_put_configuration

sig_status_t sig_project_put_configuration(sig_project * project, const char * name, const char * json, size_t len, bool * created)
ParameterType
projectsig_project *
nameconst char *
jsonconst char *
lensize_t
createdbool *

Creates (*created = true) or replaces (*created = false) a configuration. The document is also validated against its capsule's property table (see sig_property).

sig_project_patch_configuration

sig_status_t sig_project_patch_configuration(sig_project * project, const char * name, const char * patch, size_t len)
ParameterType
projectsig_project *
nameconst char *
patchconst char *
lensize_t

JSON Merge Patch (sig_value_merge_patch semantics; null is rejected with SIG_ERR_PARSE) applied to the stored definition. SIG_ERR_NOT_FOUND if the configuration does not exist.

sig_project_delete_configuration

sig_status_t sig_project_delete_configuration(sig_project * project, const char * name)
ParameterType
projectsig_project *
nameconst char *

SIG_ERR_NOT_FOUND if absent; SIG_ERR_EXISTS if any service definition still uses it.

sig_project_rename_configuration

sig_status_t sig_project_rename_configuration(sig_project * project, const char * from, const char * to)
ParameterType
projectsig_project *
fromconst char *
toconst char *

Renames a configuration, and the references to it in every service definition that uses it (the services are saved again, as compact JSON). A running service is left running; like any other edit to what it uses, the rename makes it dirty until it is restarted. Stopped services are rebuilt under the new name straight away. SIG_ERR_NOT_FOUND if `from` does not exist, SIG_ERR_EXISTS if `to` does (or equals `from`), SIG_ERR_INVALID_ARG for an invalid name. The configuration is validated like a put. The rename is a put of the new name, a put of each service, then a delete of the old name, so the project and the store agree after every step. On a failure those steps are undone, newest first, and the project is as it was. If the store keeps failing, the undo can stop part way: then both names exist and each service uses one of them -- a project that runs and loads as it stands.

sig_project_get_configuration

sig_status_t sig_project_get_configuration(const sig_project * project, const char * name, const sig_capsule_configuration ** out)
ParameterType
projectconst sig_project *
nameconst char *
outconst sig_capsule_configuration **

sig_project_get_configuration_json

sig_status_t sig_project_get_configuration_json(const sig_project * project, const char * name, char ** out, size_t * out_len)
ParameterType
projectconst sig_project *
nameconst char *
outchar **
out_lensize_t *

out is a NUL-terminated heap copy of the stored definition the caller sig_free()s; *out_len (nullable) excludes the NUL.

sig_project_walk_configurations

sig_status_t sig_project_walk_configurations(const sig_project * project, sig_project_configuration_fn fn, void * ctx)
ParameterType
projectconst sig_project *
fnsig_project_configuration_fn
ctxvoid *

Unspecified order. A non-SIG_OK return from fn stops the walk and becomes the result.

sig_project_walk_configuration_uses

sig_status_t sig_project_walk_configuration_uses(const sig_project * project, const char * configuration_name, sig_project_name_fn fn, void * ctx)
ParameterType
projectconst sig_project *
configuration_nameconst char *
fnsig_project_name_fn
ctxvoid *

Visits the name of every service whose DEFINITION uses the configuration.

---- services ----

sig_project_put_service

sig_status_t sig_project_put_service(sig_project * project, const char * name, const char * json, size_t len, bool * created)
ParameterType
projectsig_project *
nameconst char *
jsonconst char *
lensize_t
createdbool *

Creates (*created = true) or replaces (*created = false) a service definition. Replacing a RUNNING service only changes its definition; the live service keeps running unchanged and is reported dirty. A stopped service is rebuilt from the new definition immediately.

sig_project_patch_service

sig_status_t sig_project_patch_service(sig_project * project, const char * name, const char * patch, size_t len)
ParameterType
projectsig_project *
nameconst char *
patchconst char *
lensize_t

shared with sig_project_put_service

Creates (*created = true) or replaces (*created = false) a service definition. Replacing a RUNNING service only changes its definition; the live service keeps running unchanged and is reported dirty. A stopped service is rebuilt from the new definition immediately.

sig_project_delete_service

sig_status_t sig_project_delete_service(sig_project * project, const char * name)
ParameterType
projectsig_project *
nameconst char *

Stops the service if it is running, removes its key from the store and its entry. SIG_ERR_NOT_FOUND if absent.

sig_project_get_service_json

sig_status_t sig_project_get_service_json(const sig_project * project, const char * name, char ** out, size_t * out_len)
ParameterType
projectconst sig_project *
nameconst char *
outchar **
out_lensize_t *

sig_project_service_dirty

sig_status_t sig_project_service_dirty(const sig_project * project, const char * name, bool * out)
ParameterType
projectconst sig_project *
nameconst char *
outbool *

out is true when the service's definition, or the definition of a configuration it uses, changed after the live service was built -- i.e. a restart would pick up something new.

sig_project_refresh_service

sig_status_t sig_project_refresh_service(sig_project * project, const char * name, sig_service ** out)
ParameterType
projectsig_project *
nameconst char *
outsig_service **

If the service is stopped and dirty, rebuilds the live service from its definition (invalidating any earlier sig_service pointer for it). A running service is left alone. *out is the current live service.