neo_solidity/cli/cli_parts/cli_compile/
permissions.rs1fn parse_manifest_permissions_mode(value: &str) -> Result<ManifestPermissionsMode, String> {
2 match value.trim() {
3 "merge" => Ok(ManifestPermissionsMode::Merge),
4 "replace-wildcards" => Ok(ManifestPermissionsMode::ReplaceWildcards),
5 other => Err(format!(
6 "invalid manifest permissions mode '{other}' (expected 'merge' or 'replace-wildcards')"
7 )),
8 }
9}
10
11fn load_manifest_permissions_override(
12 path: &str,
13 mode: &str,
14) -> Result<ManifestPermissionsOverride, String> {
15 let mode = parse_manifest_permissions_mode(mode)?;
16 let content =
17 fs::read_to_string(path).map_err(|err| format!("Failed to read manifest permissions file '{path}': {err}"))?;
18 let root: Value =
19 serde_json::from_str(&content).map_err(|err| format!("Failed to parse manifest permissions JSON: {err}"))?;
20 let permissions_value = match root {
21 Value::Array(_) => root,
22 Value::Object(mut map) => map
23 .remove("permissions")
24 .ok_or_else(|| "manifest permissions JSON object must contain a 'permissions' array".to_string())?,
25 _ => {
26 return Err(
27 "manifest permissions JSON must be either an array or an object containing a 'permissions' array"
28 .to_string(),
29 )
30 }
31 };
32
33 let permissions = parse_manifest_permissions_array(&permissions_value)?;
34
35 Ok(ManifestPermissionsOverride { mode, permissions })
36}
37
38fn parse_manifest_permissions_array(value: &Value) -> Result<ManifestPermissionMap, String> {
39 let arr = value.as_array().ok_or_else(|| {
40 "manifest permissions must be an array of objects like {\"contract\":\"0x...\",\"methods\":[...]}"
41 .to_string()
42 })?;
43
44 let mut out: ManifestPermissionMap = ManifestPermissionMap::new();
45 for (index, entry) in arr.iter().enumerate() {
46 let obj = entry.as_object().ok_or_else(|| {
47 format!("manifest permission entry #{index} must be an object")
48 })?;
49
50 let contract_value = obj.get("contract").ok_or_else(|| {
51 format!("manifest permission entry #{index} is missing 'contract'")
52 })?;
53 let contract_raw = contract_value.as_str().ok_or_else(|| {
54 format!("manifest permission entry #{index} field 'contract' must be a string")
55 })?;
56 let contract = if contract_raw.trim() == "*" {
57 "*".to_string()
58 } else {
59 let parsed = neo_solidity::neo::parse_uint160_hex_be(contract_raw).map_err(|err| {
60 format!("manifest permission entry #{index} has invalid 'contract': {err}")
61 })?;
62 neo_solidity::neo::format_uint160_hex_be(&parsed)
63 };
64
65 let methods_value = obj.get("methods").ok_or_else(|| {
66 format!("manifest permission entry #{index} is missing 'methods'")
67 })?;
68 let methods = match methods_value {
69 Value::String(s) if s.trim() == "*" => ManifestPermissionMethods::All,
70 Value::Array(list) => {
71 let mut set = BTreeSet::new();
72 for (method_index, method) in list.iter().enumerate() {
73 let method_str = method.as_str().ok_or_else(|| {
74 format!(
75 "manifest permission entry #{index} methods[{method_index}] must be a string"
76 )
77 })?;
78 let trimmed = method_str.trim();
79 if trimmed.is_empty() {
80 return Err(format!(
81 "manifest permission entry #{index} methods[{method_index}] must not be empty"
82 ));
83 }
84 set.insert(trimmed.to_string());
85 }
86 ManifestPermissionMethods::Some(set)
87 }
88 _ => {
89 return Err(format!(
90 "manifest permission entry #{index} field 'methods' must be \"*\" or an array of strings"
91 ))
92 }
93 };
94
95 out.entry(contract)
96 .and_modify(|existing| existing.merge_in(methods.clone()))
97 .or_insert(methods);
98 }
99
100 Ok(out)
101}
102
103fn manifest_permissions_to_json(permissions: ManifestPermissionMap) -> Value {
104 Value::Array(
105 permissions
106 .into_iter()
107 .map(|(contract, methods)| {
108 let methods_json = match methods {
109 ManifestPermissionMethods::All => json!("*"),
110 ManifestPermissionMethods::Some(set) => json!(set.into_iter().collect::<Vec<_>>()),
111 };
112 json!({
113 "contract": contract,
114 "methods": methods_json,
115 })
116 })
117 .collect(),
118 )
119}
120
121fn parse_manifest_permissions_from_manifest(manifest: &Value) -> Result<ManifestPermissionMap, String> {
122 match manifest.get("permissions") {
123 Some(Value::Array(_)) => parse_manifest_permissions_array(&manifest["permissions"]),
124 Some(other) => Err(format!(
125 "manifest 'permissions' must be an array (got {})",
126 other
127 )),
128 None => Ok(ManifestPermissionMap::new()),
129 }
130}
131
132fn merge_manifest_permissions(into: &mut ManifestPermissionMap, other: &ManifestPermissionMap) {
133 for (contract, methods) in other {
134 into.entry(contract.clone())
135 .and_modify(|existing| existing.merge_in(methods.clone()))
136 .or_insert_with(|| methods.clone());
137 }
138}