static CFCParcel* S_audition_parcel(const char *version_dir, const char *vstring, CFCVersion *min_version, CFCParcel *best) { CFCVersion *version = CFCVersion_new(vstring); CFCVersion *best_version = best ? CFCParcel_get_version(best) : NULL; // Version must match min_version and be greater than the previous best. if (CFCVersion_compare_to(version, min_version) >= 0 && (best_version == NULL || CFCVersion_compare_to(version, best_version) > 0) ) { // Parse parcel JSON for major version check. CFCFileSpec *file_spec = CFCFileSpec_new(version_dir, "parcel", ".json", true); CFCParcel *parcel = CFCParcel_new_from_file(file_spec); CFCVersion *major_version = CFCParcel_get_major_version(parcel); if (CFCVersion_compare_to(major_version, min_version) <= 0) { CFCBase_decref((CFCBase*)best); best = parcel; } else { CFCBase_decref((CFCBase*)parcel); } CFCBase_decref((CFCBase*)file_spec); } CFCBase_decref((CFCBase*)version); return best; }
static void S_set_prereqs(CFCParcel *self, CFCJson *node, const char *path) { size_t num_prereqs = CFCJson_get_num_children(node) / 2; CFCJson **children = CFCJson_get_children(node); CFCPrereq **prereqs = (CFCPrereq**)MALLOCATE((num_prereqs + 1) * sizeof(CFCPrereq*)); for (size_t i = 0; i < num_prereqs; ++i) { const char *name = CFCJson_get_string(children[2*i]); CFCJson *value = children[2*i+1]; int value_type = CFCJson_get_type(value); CFCVersion *version = NULL; if (value_type == CFCJSON_STRING) { version = CFCVersion_new(CFCJson_get_string(value)); } else if (value_type != CFCJSON_NULL) { CFCUtil_die("Invalid prereq value (filepath '%s')", path); } prereqs[i] = CFCPrereq_new(name, version); CFCBase_decref((CFCBase*)version); } prereqs[num_prereqs] = NULL; // Assume that prereqs are empty. FREEMEM(self->prereqs); self->prereqs = prereqs; self->num_prereqs = num_prereqs; }
CFCParcel* CFCParcel_init(CFCParcel *self, const char *name, const char *cnick, CFCVersion *version) { // Validate name. if (!name || !S_validate_name_or_cnick(name)) { CFCUtil_die("Invalid name: '%s'", name ? name : "[NULL]"); } self->name = CFCUtil_strdup(name); // Validate or derive cnick. if (cnick) { if (!S_validate_name_or_cnick(cnick)) { CFCUtil_die("Invalid cnick: '%s'", cnick); } self->cnick = CFCUtil_strdup(cnick); } else { // Default cnick to name. self->cnick = CFCUtil_strdup(name); } // Default to version v0. if (version) { self->version = (CFCVersion*)CFCBase_incref((CFCBase*)version); } else { self->version = CFCVersion_new("v0"); } // Derive prefix, Prefix, PREFIX. size_t cnick_len = strlen(self->cnick); size_t prefix_len = cnick_len ? cnick_len + 1 : 0; size_t amount = prefix_len + 1; self->prefix = (char*)MALLOCATE(amount); self->Prefix = (char*)MALLOCATE(amount); self->PREFIX = (char*)MALLOCATE(amount); memcpy(self->Prefix, self->cnick, cnick_len); if (cnick_len) { self->Prefix[cnick_len] = '_'; self->Prefix[cnick_len + 1] = '\0'; } else { self->Prefix[cnick_len] = '\0'; } for (size_t i = 0; i < amount; i++) { self->prefix[i] = tolower(self->Prefix[i]); self->PREFIX[i] = toupper(self->Prefix[i]); } self->prefix[prefix_len] = '\0'; self->Prefix[prefix_len] = '\0'; self->PREFIX[prefix_len] = '\0'; return self; }
CFCParcel* CFCParcel_clownfish_parcel(void) { CFCParcel *parcel = CFCParcel_fetch("Lucy"); if (!parcel) { CFCVersion *version = CFCVersion_new("v0.3.0"); parcel = CFCParcel_new("Lucy", "Lucy", version); CFCParcel_register(parcel); CFCBase_decref((CFCBase*)version); CFCBase_decref((CFCBase*)parcel); } return parcel; }
static void S_run_prereq_tests(CFCTest *test) { { CFCVersion *v77_66_55 = CFCVersion_new("v77.66.55"); CFCPrereq *prereq = CFCPrereq_new("Flour", v77_66_55); const char *name = CFCPrereq_get_name(prereq); STR_EQ(test, name, "Flour", "prereq get_name"); CFCVersion *version = CFCPrereq_get_version(prereq); INT_EQ(test, CFCVersion_compare_to(version, v77_66_55), 0, "prereq get_version"); CFCBase_decref((CFCBase*)prereq); CFCBase_decref((CFCBase*)v77_66_55); } { CFCVersion *v0 = CFCVersion_new("v0"); CFCPrereq *prereq = CFCPrereq_new("Sugar", NULL); CFCVersion *version = CFCPrereq_get_version(prereq); INT_EQ(test, CFCVersion_compare_to(version, v0), 0, "prereq with default version"); CFCBase_decref((CFCBase*)prereq); CFCBase_decref((CFCBase*)v0); } }
CFCPrereq* CFCPrereq_init(CFCPrereq *self, const char *name, CFCVersion *version) { // Validate name. if (!name || !S_validate_name_or_nickname(name)) { CFCUtil_die("Invalid name: '%s'", name ? name : "[NULL]"); } self->name = CFCUtil_strdup(name); // Default to version v0. if (version) { self->version = (CFCVersion*)CFCBase_incref((CFCBase*)version); } else { self->version = CFCVersion_new("v0"); } return self; }
static CFCParcel* S_new_from_json(const char *json, const char *path) { JSONNode *parsed = S_parse_json_for_parcel(json); if (!parsed) { CFCUtil_die("Invalid JSON parcel definition in '%s'", path); } const char *name = NULL; const char *nickname = NULL; CFCVersion *version = NULL; for (size_t i = 0, max = parsed->num_kids; i < max; i += 2) { JSONNode *key = parsed->kids[i]; JSONNode *value = parsed->kids[i + 1]; if (key->type != JSON_STRING) { CFCUtil_die("JSON parsing error (filepath '%s')", path); } if (strcmp(key->string, "name") == 0) { if (value->type != JSON_STRING) { CFCUtil_die("'name' must be a string (filepath %s)", path); } name = value->string; } else if (strcmp(key->string, "nickname") == 0) { if (value->type != JSON_STRING) { CFCUtil_die("'nickname' must be a string (filepath %s)", path); } nickname = value->string; } else if (strcmp(key->string, "version") == 0) { if (value->type != JSON_STRING) { CFCUtil_die("'version' must be a string (filepath %s)", path); } version = CFCVersion_new(value->string); } } if (!name) { CFCUtil_die("Missing required key 'name' (filepath '%s')", path); } if (!version) { CFCUtil_die("Missing required key 'version' (filepath '%s')", path); } CFCParcel *self = CFCParcel_new(name, nickname, version); CFCBase_decref((CFCBase*)version); for (size_t i = 0, max = parsed->num_kids; i < max; i += 2) { JSONNode *key = parsed->kids[i]; JSONNode *value = parsed->kids[i + 1]; if (strcmp(key->string, "name") == 0 || strcmp(key->string, "nickname") == 0 || strcmp(key->string, "version") == 0 ) { ; } else { CFCUtil_die("Unrecognized key: '%s' (filepath '%s')", key->string, path); } } S_destroy_json(parsed); return self; }
static void S_run_parcel_tests(CFCTest *test) { { CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, NULL); OK(test, parcel != NULL, "new"); OK(test, !CFCParcel_included(parcel), "not included"); CFCBase_decref((CFCBase*)parcel); } { CFCFileSpec *file_spec = CFCFileSpec_new(".", "Parcel", true); CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, file_spec); OK(test, CFCParcel_included(parcel), "included"); CFCBase_decref((CFCBase*)parcel); CFCBase_decref((CFCBase*)file_spec); } { const char *json = " {\n" " \"name\": \"Crustacean\",\n" " \"nickname\": \"Crust\",\n" " \"version\": \"v0.1.0\"\n" " }\n"; CFCParcel *parcel = CFCParcel_new_from_json(json, NULL); OK(test, parcel != NULL, "new_from_json"); CFCBase_decref((CFCBase*)parcel); } { const char *path = "t" CHY_DIR_SEP "cfbase" CHY_DIR_SEP "Animal.cfp"; CFCParcel *parcel = CFCParcel_new_from_file(path, NULL); OK(test, parcel != NULL, "new_from_file"); CFCBase_decref((CFCBase*)parcel); } { CFCParcel *parcel = CFCParcel_new("Crustacean", "Crust", NULL, NULL); CFCParcel_register(parcel); STR_EQ(test, CFCVersion_get_vstring(CFCParcel_get_version(parcel)), "v0", "get_version"); CFCBase_decref((CFCBase*)parcel); CFCParcel_reap_singletons(); } { const char *json = " {\n" " \"name\": \"Crustacean\",\n" " \"version\": \"v0.1.0\",\n" " \"prerequisites\": {\n" " \"Clownfish\": null,\n" " \"Arthropod\": \"v30.104.5\"\n" " }\n" " }\n"; CFCParcel *parcel = CFCParcel_new_from_json(json, NULL); CFCPrereq **prereqs = CFCParcel_get_prereqs(parcel); OK(test, prereqs != NULL, "prereqs"); CFCPrereq *cfish = prereqs[0]; OK(test, cfish != NULL, "prereqs[0]"); const char *cfish_name = CFCPrereq_get_name(cfish); STR_EQ(test, cfish_name, "Clownfish", "prereqs[0] name"); CFCVersion *v0 = CFCVersion_new("v0"); CFCVersion *cfish_version = CFCPrereq_get_version(cfish); INT_EQ(test, CFCVersion_compare_to(cfish_version, v0), 0, "prereqs[0] version"); CFCPrereq *apod = prereqs[1]; OK(test, apod != NULL, "prereqs[1]"); const char *apod_name = CFCPrereq_get_name(apod); STR_EQ(test, apod_name, "Arthropod", "prereqs[1] name"); CFCVersion *v30_104_5 = CFCVersion_new("v30.104.5"); CFCVersion *apod_version = CFCPrereq_get_version(apod); INT_EQ(test, CFCVersion_compare_to(apod_version, v30_104_5), 0, "prereqs[1] version"); OK(test, prereqs[2] == NULL, "prereqs[2]"); CFCBase_decref((CFCBase*)v30_104_5); CFCBase_decref((CFCBase*)v0); CFCBase_decref((CFCBase*)parcel); } { CFCFileSpec *foo_file_spec = CFCFileSpec_new(".", "Foo", true); CFCParcel *foo = CFCParcel_new("Foo", NULL, NULL, foo_file_spec); CFCParcel_register(foo); CFCVersion *cfish_version = CFCVersion_new("v0.8.7"); CFCFileSpec *cfish_file_spec = CFCFileSpec_new(".", "Clownfish", true); CFCParcel *cfish = CFCParcel_new("Clownfish", NULL, cfish_version, cfish_file_spec); CFCParcel_register(cfish); const char *crust_json = " {\n" " \"name\": \"Crustacean\",\n" " \"version\": \"v0.1.0\",\n" " \"prerequisites\": {\n" " \"Clownfish\": \"v0.8.5\",\n" " }\n" " }\n"; CFCParcel *crust = CFCParcel_new_from_json(crust_json, NULL); CFCParcel_register(crust); CFCParcel_check_prereqs(crust); INT_EQ(test, CFCParcel_required(foo), false, "parcel not required"); INT_EQ(test, CFCParcel_required(cfish), true, "prereq required"); INT_EQ(test, CFCParcel_required(crust), true, "self required"); CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(crust); OK(test, prereq_parcels[0] != NULL, "prereq_parcels[0]"); const char *name = CFCParcel_get_name(prereq_parcels[0]); STR_EQ(test, name, "Clownfish", "prereq_parcels[0] name"); OK(test, prereq_parcels[1] == NULL, "prereq_parcels[0]"); OK(test, CFCParcel_has_prereq(crust, cfish), "has_prereq"); OK(test, CFCParcel_has_prereq(crust, crust), "has_prereq self"); OK(test, !CFCParcel_has_prereq(crust, foo), "has_prereq false"); CFCParcel_add_struct_sym(cfish, "Swim"); CFCParcel_add_struct_sym(crust, "Pinch"); CFCParcel_add_struct_sym(foo, "Bar"); CFCParcel *found; found = CFCParcel_lookup_struct_sym(crust, "Swim"); OK(test, found == cfish, "lookup_struct_sym prereq"); found = CFCParcel_lookup_struct_sym(crust, "Pinch"); OK(test, found == crust, "lookup_struct_sym self"); found = CFCParcel_lookup_struct_sym(crust, "Bar"); OK(test, found == NULL, "lookup_struct_sym other"); FREEMEM(prereq_parcels); CFCBase_decref((CFCBase*)crust); CFCBase_decref((CFCBase*)cfish_version); CFCBase_decref((CFCBase*)cfish_file_spec); CFCBase_decref((CFCBase*)cfish); CFCBase_decref((CFCBase*)foo_file_spec); CFCBase_decref((CFCBase*)foo); CFCParcel_reap_singletons(); } }
static CFCParcel* S_new_from_json(const char *json, CFCFileSpec *file_spec) { const char *path = file_spec ? CFCFileSpec_get_path(file_spec) : "[NULL]"; CFCJson *parsed = CFCJson_parse(json); if (!parsed) { CFCUtil_die("Invalid JSON parcel definition in '%s'", path); } if (CFCJson_get_type(parsed) != CFCJSON_HASH) { CFCUtil_die("Parcel definition must be a hash in '%s'", path); } const char *name = NULL; const char *nickname = NULL; int installed = true; CFCVersion *version = NULL; CFCVersion *major_version = NULL; CFCJson *prereqs = NULL; CFCJson **children = CFCJson_get_children(parsed); for (size_t i = 0; children[i]; i += 2) { const char *key = CFCJson_get_string(children[i]); CFCJson *value = children[i + 1]; int value_type = CFCJson_get_type(value); if (strcmp(key, "name") == 0) { if (value_type != CFCJSON_STRING) { CFCUtil_die("'name' must be a string (filepath %s)", path); } name = CFCJson_get_string(value); } else if (strcmp(key, "nickname") == 0) { if (value_type != CFCJSON_STRING) { CFCUtil_die("'nickname' must be a string (filepath %s)", path); } nickname = CFCJson_get_string(value); } else if (strcmp(key, "installed") == 0) { if (value_type != CFCJSON_BOOL) { CFCUtil_die("'installed' must be a boolean (filepath %s)", path); } installed = CFCJson_get_bool(value); } else if (strcmp(key, "version") == 0) { if (value_type != CFCJSON_STRING) { CFCUtil_die("'version' must be a string (filepath %s)", path); } version = CFCVersion_new(CFCJson_get_string(value)); } else if (strcmp(key, "major_version") == 0) { if (value_type != CFCJSON_STRING) { CFCUtil_die("'major_version' must be a string (filepath %s)", path); } major_version = CFCVersion_new(CFCJson_get_string(value)); } else if (strcmp(key, "prerequisites") == 0) { if (value_type != CFCJSON_HASH) { CFCUtil_die("'prerequisites' must be a hash (filepath %s)", path); } prereqs = value; } else { CFCUtil_die("Unrecognized key: '%s' (filepath '%s')", key, path); } } if (!name) { CFCUtil_die("Missing required key 'name' (filepath '%s')", path); } if (!version) { CFCUtil_die("Missing required key 'version' (filepath '%s')", path); } CFCParcel *self = CFCParcel_new(name, nickname, version, major_version, file_spec); if (!file_spec || !CFCFileSpec_included(file_spec)) { self->is_installed = installed; } if (prereqs) { S_set_prereqs(self, prereqs, path); } CFCBase_decref((CFCBase*)version); CFCBase_decref((CFCBase*)major_version); CFCJson_destroy(parsed); return self; }
CFCParcel* CFCParcel_init(CFCParcel *self, const char *name, const char *nickname, CFCVersion *version, CFCVersion *major_version, CFCFileSpec *file_spec) { // Validate name. if (!name || !S_validate_name_or_nickname(name)) { CFCUtil_die("Invalid name: '%s'", name ? name : "[NULL]"); } self->name = CFCUtil_strdup(name); // Validate or derive nickname. if (nickname) { if (!S_validate_name_or_nickname(nickname)) { CFCUtil_die("Invalid nickname: '%s'", nickname); } self->nickname = CFCUtil_strdup(nickname); } else { // Default nickname to name. self->nickname = CFCUtil_strdup(name); } // Default to version v0. if (version) { self->version = (CFCVersion*)CFCBase_incref((CFCBase*)version); } else { self->version = CFCVersion_new("v0"); } if (major_version) { self->major_version = (CFCVersion*)CFCBase_incref((CFCBase*)major_version); } else { self->major_version = CFCVersion_new("v0"); } // Set file_spec. self->file_spec = (CFCFileSpec*)CFCBase_incref((CFCBase*)file_spec); // Derive prefix, Prefix, PREFIX. size_t nickname_len = strlen(self->nickname); size_t prefix_len = nickname_len ? nickname_len + 1 : 0; size_t amount = prefix_len + 1; self->prefix = (char*)MALLOCATE(amount); self->Prefix = (char*)MALLOCATE(amount); self->PREFIX = (char*)MALLOCATE(amount); memcpy(self->Prefix, self->nickname, nickname_len); if (nickname_len) { self->Prefix[nickname_len] = '_'; self->Prefix[nickname_len + 1] = '\0'; } else { self->Prefix[nickname_len] = '\0'; } for (size_t i = 0; i < amount; i++) { self->prefix[i] = CFCUtil_tolower(self->Prefix[i]); self->PREFIX[i] = CFCUtil_toupper(self->Prefix[i]); } self->prefix[prefix_len] = '\0'; self->Prefix[prefix_len] = '\0'; self->PREFIX[prefix_len] = '\0'; // Derive privacy symbol. size_t privacy_sym_len = nickname_len + 4; self->privacy_sym = (char*)MALLOCATE(privacy_sym_len + 1); memcpy(self->privacy_sym, "CFP_", 4); for (size_t i = 0; i < nickname_len; i++) { self->privacy_sym[i+4] = CFCUtil_toupper(self->nickname[i]); } self->privacy_sym[privacy_sym_len] = '\0'; // Initialize flags. self->is_installed = false; // Initialize arrays. self->classes = (CFCClass**)CALLOCATE(1, sizeof(CFCClass*)); self->num_classes = 0; self->prereqs = (CFCPrereq**)CALLOCATE(1, sizeof(CFCPrereq*)); self->num_prereqs = 0; return self; }