static CFCClass* S_class_by_struct_sym_prereq(CFCParcel *self, const char *struct_sym, size_t prefix_len) { CFCClass *klass = S_class_by_struct_sym(self, struct_sym, prefix_len); if (klass && prefix_len != 0) { return klass; } for (size_t i = 0; self->prereqs[i]; ++i) { const char *prereq_name = CFCPrereq_get_name(self->prereqs[i]); CFCParcel *prereq_parcel = CFCParcel_fetch(prereq_name); CFCClass *candidate = S_class_by_struct_sym(prereq_parcel, struct_sym, prefix_len); if (candidate) { if (prefix_len != 0) { return candidate; } if (klass) { CFCUtil_warn("Type '%s' is ambiguous. Do you mean %s or %s?", struct_sym, CFCClass_full_struct_sym(klass), CFCClass_full_struct_sym(candidate)); return NULL; } klass = candidate; } } return klass; }
static CFCClass* S_fetch_class(CFCParcel *self, const char *class_name, int level) { // level == 0: Only search parcel. // level == 1: Search parcel and direct prereqs. // level == 2: Search parcel an indirect prereqs. for (size_t i = 0; self->classes[i]; ++i) { CFCClass *klass = self->classes[i]; if (strcmp(CFCClass_get_name(klass), class_name) == 0) { return klass; } } if (level == 0) { return NULL; } if (level == 1) { level = 0; } for (size_t i = 0; self->prereqs[i]; ++i) { const char *prereq_name = CFCPrereq_get_name(self->prereqs[i]); CFCParcel *prereq_parcel = CFCParcel_fetch(prereq_name); CFCClass *klass = S_fetch_class(prereq_parcel, class_name, level); if (klass) { return klass; } } return NULL; }
int CFCParcel_has_prereq(CFCParcel *self, CFCParcel *parcel) { const char *name = CFCParcel_get_name(parcel); if (strcmp(CFCParcel_get_name(self), name) == 0) { return true; } for (int i = 0; self->prereqs[i]; ++i) { const char *prereq_name = CFCPrereq_get_name(self->prereqs[i]); if (strcmp(prereq_name, name) == 0) { return true; } } return false; }
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); } }
static void S_find_prereq(CFCHierarchy *self, CFCParcel *parent, CFCPrereq *prereq) { const char *name = CFCPrereq_get_name(prereq); CFCVersion *min_version = CFCPrereq_get_version(prereq); // Check whether prereq was processed already. CFCParcel **parcels = CFCParcel_all_parcels(); for (int i = 0; parcels[i]; ++i) { CFCParcel *parcel = parcels[i]; const char *other_name = CFCParcel_get_name(parcel); if (strcmp(other_name, name) == 0) { CFCVersion *other_version = CFCParcel_get_version(parcel); CFCVersion *major_version = CFCParcel_get_major_version(parcel); if (CFCVersion_compare_to(major_version, min_version) <= 0 && CFCVersion_compare_to(min_version, other_version) <= 0 ) { // Compatible version found. return; } else { CFCUtil_die("Parcel %s %s required by %s not compatible with" " version %s required by %s", name, other_version, "[TODO]", CFCVersion_get_vstring(min_version), CFCParcel_get_name(parent)); } } } CFCParcel *parcel = NULL; // TODO: Decide whether to prefer higher versions from directories // that come later in the list of include dirs or stop processing once // a suitable version was found in a dir. for (size_t i = 0; self->includes[i] != NULL; i++) { char *name_dir = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s", self->includes[i], name); if (CFCUtil_is_dir(name_dir)) { void *dirhandle = CFCUtil_opendir(name_dir); const char *entry = NULL; while (NULL != (entry = CFCUtil_dirnext(dirhandle))) { if (!CFCVersion_is_vstring(entry)) { continue; } char *version_dir = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s", name_dir, entry); if (CFCUtil_is_dir(version_dir)) { parcel = S_audition_parcel(version_dir, entry, min_version, parcel); } FREEMEM(version_dir); } CFCUtil_closedir(dirhandle, name_dir); } FREEMEM(name_dir); } if (parcel == NULL) { CFCUtil_die("Parcel %s %s required by %s not found", name, CFCVersion_get_vstring(min_version), CFCParcel_get_name(parent)); } // Make sure to register prereq parcels first, so that prereq // parent classes precede subclasses. S_find_prereqs(self, parcel); CFCParcel_register(parcel); CFCBase_decref((CFCBase*)parcel); }
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(); } }