// file : bpkg/manifest -*- C++ -*- // copyright : Copyright (c) 2014-2015 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #ifndef BPKG_MANIFEST #define BPKG_MANIFEST #include #include #include // uint16_t #include #include // move() #include // logic_error #include #include namespace bpkg { class manifest_parser; class manifest_serializer; class manifest_name_value; using strings = std::vector; class version { public: // Let's keep the members in the order they appear in the string // representation. // const std::uint16_t epoch; const std::string upstream; const std::uint16_t revision; // Upstream part canonical representation. // const std::string canonical_upstream; // Create a special empty version. // version (): epoch (0), revision (0) {} // Throw std::invalid_argument if the passed string is not a valid // version representation. // explicit version (const std::string& v): version (v.c_str ()) {} explicit version (const char* v): version (data_type (v, false)) {} // Create the version object from separate epoch, upstream, and // revision parts. // version (std::uint16_t epoch, std::string upstream, std::uint16_t revision); version (version&&) = default; version (const version&) = default; version& operator= (version&&); version& operator= (const version&); std::string string (bool ignore_revision = false) const; bool operator< (const version& v) const noexcept {return compare (v) < 0;} bool operator> (const version& v) const noexcept {return compare (v) > 0;} bool operator== (const version& v) const noexcept {return compare (v) == 0;} bool operator<= (const version& v) const noexcept {return compare (v) <= 0;} bool operator>= (const version& v) const noexcept {return compare (v) >= 0;} bool operator!= (const version& v) const noexcept {return compare (v) != 0;} int compare (const version& v, bool ignore_revision = false) const noexcept { if (epoch != v.epoch) return epoch < v.epoch ? -1 : 1; if (int c = canonical_upstream.compare (v.canonical_upstream)) return c; if (!ignore_revision && revision != v.revision) return revision < v.revision ? -1 : 1; return 0; } bool empty () const noexcept { // No sense to test epoch and revision for 0 as properly constructed // version object can not have them different from 0 if upstream is // empty. // return upstream.empty (); } private: struct data_type { data_type (const char*, bool upstream_only); std::uint16_t epoch; std::string upstream; std::uint16_t revision; std::string canonical_upstream; }; explicit version (data_type&& d) : epoch (d.epoch), upstream (std::move (d.upstream)), revision (d.revision), canonical_upstream (std::move (d.canonical_upstream)) {} }; inline std::ostream& operator<< (std::ostream& os, const version& v) {return os << v.string ();} // priority // class priority { public: enum value_type {low, medium, high, security}; value_type value; // Shouldn't be necessary to access directly. std::string comment; priority (value_type v = low, std::string c = "") : value (v), comment (std::move (c)) {} operator value_type () const {return value;} }; // description // description-file // class description: public std::string { public: bool file; std::string comment; // Description constructor. // explicit description (std::string d = "") : std::string (std::move (d)), file (false) {} // Description file constructor. // description (std::string f, std::string c) : std::string (std::move (f)), file (true), comment (std::move (c)) {} }; // license // class licenses: public strings { public: std::string comment; explicit licenses (std::string c = ""): comment (std::move (c)) {} }; // change // change-file // class change: public std::string { public: bool file; std::string comment; // Change constructor. // explicit change (std::string c = ""): std::string (std::move (c)), file (false) {} // Change file constructor. // change (std::string f, std::string c) : std::string (std::move (f)), file (true), comment (std::move (c)) {} }; // url // package-url // class url: public std::string { public: std::string comment; explicit url (std::string u = "", std::string c = "") : std::string (std::move (u)), comment (std::move (c)) {} }; // email // package-email // class email: public std::string { public: std::string comment; explicit email (std::string e = "", std::string c = "") : std::string (std::move (e)), comment (std::move (c)) {} }; // depends // enum class comparison {eq, lt, gt, le, ge}; std::string to_string (comparison); comparison to_comparison (const std::string&); // May throw invalid_argument. inline std::ostream& operator<< (std::ostream& os, comparison c) {return os << to_string (c);} struct dependency_constraint { comparison operation; bpkg::version version; }; std::ostream& operator<< (std::ostream&, const dependency_constraint&); struct dependency { std::string name; butl::optional constraint; }; std::ostream& operator<< (std::ostream&, const dependency&); class dependency_alternatives: public std::vector { public: bool conditional; std::string comment; explicit dependency_alternatives (bool d = false, std::string c = "") : conditional (d), comment (std::move (c)) {} }; std::ostream& operator<< (std::ostream&, const dependency_alternatives&); // requires // class requirement_alternatives: public strings { public: bool conditional; std::string comment; explicit requirement_alternatives (bool d = false, std::string c = "") : conditional (d), comment (std::move (c)) {} }; class package_manifest { public: using version_type = bpkg::version; using priority_type = bpkg::priority; using url_type = bpkg::url; using email_type = bpkg::email; using description_type = bpkg::description; std::string name; version_type version; butl::optional priority; std::string summary; std::vector license_alternatives; strings tags; butl::optional description; std::vector changes; url_type url; butl::optional package_url; email_type email; butl::optional package_email; std::vector dependencies; std::vector requirements; // The following values are only valid in the manifest list. // butl::optional location; public: package_manifest (manifest_parser&, bool ignore_unknown = false); package_manifest (manifest_parser&, manifest_name_value start, bool ignore_unknown = false); void serialize (manifest_serializer&) const; }; class package_manifests: public std::vector { public: using base_type = std::vector; using base_type::base_type; package_manifests () = default; package_manifests (manifest_parser&, bool ignore_unknown = false); void serialize (manifest_serializer&) const; }; class repository_location { public: // Create a special empty repository_location. // repository_location () = default; // If the argument is not empty, create remote/absolute repository // location. Throw invalid_argument if the location is a relative // path. If the argument is empty, then create the special empty // location. // explicit repository_location (const std::string&); // Create a potentially relative repository location. If base is not // empty, use it to complete the relative location to remote/absolute. // Throw invalid_argument if base is not empty but the location is // empty, base itself is relative, or the resulting completed location // is invalid. // repository_location (const std::string&, const repository_location& base); repository_location (const repository_location& l, const repository_location& base) : repository_location (l.string (), base) {} // Note that relative locations have no canonical name. Canonical // name of an empty location is the empty name. // const std::string& canonical_name () const noexcept {return canonical_name_;} // There are 3 types of locations: remote, local absolute filesystem // path and local relative filesystem path. Plus there is the special // empty location. The following predicates can be used to determine // what kind of location it is. Note that except for empty(), all the // other predicates throw std::logic_error for an empty location. // bool empty () const noexcept {return path_.empty ();} bool local () const { if (empty ()) throw std::logic_error ("empty location"); return host_.empty (); } bool remote () const { return !local (); } bool absolute () const { if (empty ()) throw std::logic_error ("empty location"); // Note that in remote locations path is always relative. // return path_.absolute (); } bool relative () const { return local () && path_.relative (); } const butl::dir_path& path () const { if (empty ()) throw std::logic_error ("empty location"); return path_; } const std::string& host () const { if (local ()) throw std::logic_error ("local location"); return host_; } // Value 0 indicated that no port was specified explicitly. // std::uint16_t port () const { if (local ()) throw std::logic_error ("local location"); return port_; } // Note that this is not necessarily syntactically the same string // as what was used to initialize this location. But it should be // semantically equivalent. String representation of an empty // location is the empty string. // std::string string () const; private: std::string canonical_name_; std::string host_; std::uint16_t port_; butl::dir_path path_; }; inline std::ostream& operator<< (std::ostream& os, const repository_location& l) { return os << l.string (); } enum class repository_role { base, prerequisite, complement }; class repository_manifest { public: repository_location location; butl::optional role; // The following values may only be present for the base repository. // butl::optional url; butl::optional email; butl::optional summary; butl::optional description; public: repository_manifest (manifest_parser&, bool ignore_unknown = false); repository_manifest (manifest_parser&, manifest_name_value start, bool ignore_unknown = false); void serialize (manifest_serializer&) const; // Return the effective role of the repository. If the role is not // explicitly specified (see the role member above), then calculate // the role based on the location. Specifically, if the location is // empty, then the effective role is base. Otherwise -- prerequisite. // If the role is specified, then verify that it is consistent with // the location value (that is, base if the location is empty and // prerequisite or complete if not) and return that. Otherwise, // throw logic_error. // repository_role effective_role () const; }; class repository_manifests: public std::vector { public: using base_type = std::vector; using base_type::base_type; repository_manifests () = default; repository_manifests (manifest_parser&, bool ignore_unknown = false); void serialize (manifest_serializer&) const; }; } #endif // BPKG_MANIFEST