Skip to main content

asc_hap/
lib.rs

1//! HarmonyOS package metadata: the descriptors at the root of a HAP/HSP,
2//! read from bytes the caller supplies (no zip or file IO here).
3//!
4//! - [`ModuleJson`]: `module.json`, the manifest. Abilities and extension
5//!   abilities with their `exported` flag, required permissions, skills
6//!   and deep-link URIs; requested and defined permissions.
7//! - [`PackInfo`]: `pack.info`, the packing tool's digest of the bundle.
8//! - [`PkgContextInfo`]: `pkgContextInfo.json`, the package table that
9//!   `.abc` record names and import requests refer to.
10//! - [`PageList`] and [`RouterMap`]: the page list and the navigation route
11//!   table `module.json` names (`$profile:main_pages`, `$profile:router_map`).
12//! - [`ResourceIndex`]: `resources.index`, the compiled resource table:
13//!   what the ids in compiled `$r(...)` calls and the `$string:name`
14//!   references of `module.json` stand for.
15//! - [`SourceMaps`]: `ets/sourceMaps.map` of a debug build, from the lines of
16//!   the compiled files the code was made from to the source.
17//!
18//! Every JSON type offers the same two texts: `summary()`, one fact per line
19//! for `grep`, and `pretty()`, the document itself re-indented. The
20//! summaries state what the documents say and pass no judgement.
21//!
22//! Parsing the JSON documents is tolerant: only input that is not a JSON object is an error.
23//! A wrongly typed field is skipped and recorded in `notes`; a field the
24//! model does not cover is listed in `unmodelled` and stays reachable
25//! through `raw()`.
26//!
27//! ```
28//! use asc_hap::ModuleJson;
29//!
30//! let manifest = ModuleJson::parse(
31//!     br#"{"app": {"bundleName": "com.x.y"},
32//!          "module": {"name": "entry", "abilities": [{
33//!              "name": "EntryAbility", "exported": true, "srcEntry": "./ets/entryability/EntryAbility.ets",
34//!              "skills": [{"actions": ["ohos.want.action.viewData"], "uris": [{"scheme": "xy", "host": "open"}]}]}]}}"#,
35//! )?;
36//! let ability = &manifest.module.abilities[0];
37//! assert!(ability.is_exported());
38//! assert_eq!(ability.deep_links(), ["xy://open"]);
39//! assert!(manifest.summary().contains("deeplink EntryAbility xy://open\n"));
40//! let records = manifest.record_names("./ets/entryability/EntryAbility.ets", None);
41//! assert_eq!(records.candidates(), ["com.x.y/entry/ets/entryability/EntryAbility", "&entry/src/main/ets/entryability/EntryAbility&"]);
42//! # Ok::<(), asc_hap::HapError>(())
43//! ```
44
45mod module;
46mod pack;
47mod pkg_context;
48mod profile;
49mod record;
50mod resource;
51mod source_map;
52mod value;
53
54pub use module::{
55    Ability, AbilityKind, ApiVersion, App, AtomicService, DefinePermission, Dependency, Metadata, Module, ModuleJson, MultiAppMode,
56    NameValue, ProxyData, RequestPermission, Skill, SkillUri, TestRunner,
57};
58pub use pack::{PackAbility, PackApp, PackExtension, PackForm, PackInfo, PackModule, PackPackage};
59pub use pkg_context::{PkgContext, PkgContextInfo, RecordOwner};
60pub use profile::{PageList, Route, RouterMap};
61pub use record::RecordNames;
62pub use resource::{
63    Attr, IndexFormat, Qualifier, QualifierKey, Qualifiers, Resource, ResourceIndex, ResourceIndexError, ResourceRef, ResourceType,
64    ResourceValue, Resources, UnknownResourceType,
65};
66pub use source_map::{ModuleMap, SourceLine, SourceMapError, SourceMaps};
67pub use value::Note;
68
69/// Why a metadata document could not be read at all.
70#[derive(Debug, thiserror::Error)]
71#[non_exhaustive]
72pub enum HapError {
73    /// Not JSON: empty, truncated, malformed, or nested past
74    /// `serde_json`'s recursion limit.
75    #[error("invalid JSON: {0}")]
76    Json(#[from] serde_json::Error),
77    /// The document parsed but its root is an array, string or other non-object.
78    #[error("expected a JSON object at the root, found {found}")]
79    NotAnObject {
80        /// JSON type of the root value.
81        found: &'static str,
82    },
83}
84
85fn join_lines(lines: &[String]) -> String {
86    lines.iter().flat_map(|line| [line.as_str(), "\n"]).collect()
87}
88
89#[cfg(test)]
90mod tests;