1use {
6 super::{
7 file::FileValue, python_extension_module::PythonExtensionModuleValue,
8 python_module_source::PythonModuleSourceValue,
9 python_package_distribution_resource::PythonPackageDistributionResourceValue,
10 python_package_resource::PythonPackageResourceValue,
11 python_packaging_policy::PythonPackagingPolicyValue,
12 },
13 python_packaging::{
14 location::ConcreteResourceLocation, resource::PythonResource,
15 resource_collection::PythonResourceAddCollectionContext,
16 },
17 starlark::{
18 environment::TypeValues,
19 eval::call_stack::CallStack,
20 values::{
21 error::{
22 RuntimeError, UnsupportedOperation, ValueError, INCORRECT_PARAMETER_TYPE_ERROR_CODE,
23 },
24 none::NoneType,
25 {Value, ValueResult},
26 },
27 },
28};
29
30#[derive(Clone, Debug)]
31pub struct OptionalResourceLocation {
32 inner: Option<ConcreteResourceLocation>,
33}
34
35impl From<&OptionalResourceLocation> for Value {
36 fn from(location: &OptionalResourceLocation) -> Self {
37 match &location.inner {
38 Some(ConcreteResourceLocation::InMemory) => Value::from("in-memory"),
39 Some(ConcreteResourceLocation::RelativePath(prefix)) => {
40 Value::from(format!("filesystem-relative:{}", prefix))
41 }
42 None => Value::from(NoneType::None),
43 }
44 }
45}
46
47impl TryFrom<&str> for OptionalResourceLocation {
48 type Error = ValueError;
49
50 fn try_from(s: &str) -> Result<Self, Self::Error> {
51 if s == "default" {
52 Ok(OptionalResourceLocation { inner: None })
53 } else if s == "in-memory" {
54 Ok(OptionalResourceLocation {
55 inner: Some(ConcreteResourceLocation::InMemory),
56 })
57 } else if s.starts_with("filesystem-relative:") {
58 let prefix = s.split_at("filesystem-relative:".len()).1;
59 Ok(OptionalResourceLocation {
60 inner: Some(ConcreteResourceLocation::RelativePath(prefix.to_string())),
61 })
62 } else {
63 Err(ValueError::from(RuntimeError {
64 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
65 message: format!("unable to convert value {} to a resource location", s),
66 label: format!(
67 "expected `default`, `in-memory`, or `filesystem-relative:*`; got {}",
68 s
69 ),
70 }))
71 }
72 }
73}
74
75impl TryFrom<&Value> for OptionalResourceLocation {
76 type Error = ValueError;
77
78 fn try_from(value: &Value) -> Result<Self, Self::Error> {
79 match value.get_type() {
80 "NoneType" => Ok(OptionalResourceLocation { inner: None }),
81 "string" => {
82 let s = value.to_str();
83 Ok(OptionalResourceLocation::try_from(s.as_str())?)
84 }
85 t => Err(ValueError::from(RuntimeError {
86 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
87 message: format!("unable to convert value {} to resource location", t),
88 label: "resource location conversion".to_string(),
89 })),
90 }
91 }
92}
93
94impl From<OptionalResourceLocation> for Option<ConcreteResourceLocation> {
95 fn from(location: OptionalResourceLocation) -> Self {
96 location.inner
97 }
98}
99
100pub trait ResourceCollectionContext {
102 fn add_collection_context(
104 &self,
105 ) -> Result<Option<PythonResourceAddCollectionContext>, ValueError>;
106
107 fn replace_add_collection_context(
111 &mut self,
112 add_context: PythonResourceAddCollectionContext,
113 ) -> Result<Option<PythonResourceAddCollectionContext>, ValueError>;
114
115 fn as_python_resource(&self) -> Result<PythonResource, ValueError>;
117
118 fn add_collection_context_attrs(&self) -> Vec<&'static str> {
120 vec![
121 "add_include",
122 "add_location",
123 "add_location_fallback",
124 "add_source",
125 "add_bytecode_optimization_level_zero",
126 "add_bytecode_optimization_level_one",
127 "add_bytecode_optimization_level_two",
128 ]
129 }
130
131 fn get_attr_add_collection_context(&self, attribute: &str) -> ValueResult {
136 if !self.add_collection_context_attrs().contains(&attribute) {
137 panic!(
138 "get_attr_add_collection_context({}) called when it shouldn't have been",
139 attribute
140 );
141 }
142
143 let context = self.add_collection_context()?;
144
145 Ok(match context {
146 Some(context) => match attribute {
147 "add_bytecode_optimization_level_zero" => Value::new(context.optimize_level_zero),
148 "add_bytecode_optimization_level_one" => Value::new(context.optimize_level_one),
149 "add_bytecode_optimization_level_two" => Value::new(context.optimize_level_two),
150 "add_include" => Value::new(context.include),
151 "add_location" => Value::new::<String>(context.location.into()),
152 "add_location_fallback" => match context.location_fallback.as_ref() {
153 Some(location) => Value::new::<String>(location.clone().into()),
154 None => Value::from(NoneType::None),
155 },
156 "add_source" => Value::new(context.store_source),
157 _ => panic!("this should not happen"),
158 },
159 None => Value::from(NoneType::None),
160 })
161 }
162
163 fn set_attr_add_collection_context(
166 &mut self,
167 attribute: &str,
168 value: Value,
169 ) -> Result<(), ValueError> {
170 let mut context = self.add_collection_context()?;
171
172 match context {
173 Some(ref mut context) => {
174 match attribute {
175 "add_bytecode_optimization_level_zero" => {
176 context.optimize_level_zero = value.to_bool();
177 Ok(())
178 }
179 "add_bytecode_optimization_level_one" => {
180 context.optimize_level_one = value.to_bool();
181 Ok(())
182 }
183 "add_bytecode_optimization_level_two" => {
184 context.optimize_level_two = value.to_bool();
185 Ok(())
186 }
187 "add_include" => {
188 context.include = value.to_bool();
189 Ok(())
190 }
191 "add_location" => {
192 let location: OptionalResourceLocation = (&value).try_into()?;
193
194 match location.inner {
195 Some(location) => {
196 context.location = location;
197
198 Ok(())
199 }
200 None => {
201 Err(ValueError::OperationNotSupported {
202 op: UnsupportedOperation::SetAttr(attribute.to_string()),
203 left: "set_attr".to_string(),
204 right: None,
205 })
206 }
207 }
208 }
209 "add_location_fallback" => {
210 let location: OptionalResourceLocation = (&value).try_into()?;
211
212 match location.inner {
213 Some(location) => {
214 context.location_fallback = Some(location);
215 Ok(())
216 }
217 None => {
218 context.location_fallback = None;
219 Ok(())
220 }
221 }
222 }
223 "add_source" => {
224 context.store_source = value.to_bool();
225 Ok(())
226 }
227 attr => panic!("set_attr_add_collection_context({}) called when it shouldn't have been", attr)
228 }
229 },
230 None => Err(ValueError::from(RuntimeError {
231 code: "PYOXIDIZER",
232 message: "attempting to set a collection context attribute on an object without a context".to_string(),
233 label: "setattr()".to_string()
234 }))
235 }?;
236
237 self.replace_add_collection_context(context.unwrap())?;
238
239 Ok(())
240 }
241}
242
243pub fn is_resource_starlark_compatible(resource: &PythonResource) -> bool {
245 match resource {
246 PythonResource::ModuleSource(_) => true,
247 PythonResource::PackageResource(_) => true,
248 PythonResource::PackageDistributionResource(_) => true,
249 PythonResource::ExtensionModule(_) => true,
250 PythonResource::ModuleBytecode(_) => false,
251 PythonResource::ModuleBytecodeRequest(_) => false,
252 PythonResource::EggFile(_) => false,
253 PythonResource::PathExtension(_) => false,
254 PythonResource::File(_) => true,
255 }
256}
257
258pub fn python_resource_to_value(
259 label: &str,
260 type_values: &TypeValues,
261 call_stack: &mut CallStack,
262 resource: &PythonResource,
263 policy: &PythonPackagingPolicyValue,
264) -> ValueResult {
265 match resource {
266 PythonResource::ModuleSource(sm) => {
267 let mut m = PythonModuleSourceValue::new(sm.clone().into_owned());
268 policy.apply_to_resource(label, type_values, call_stack, &mut m)?;
269
270 Ok(Value::new(m))
271 }
272
273 PythonResource::PackageResource(data) => {
274 let mut r = PythonPackageResourceValue::new(data.clone().into_owned());
275 policy.apply_to_resource(label, type_values, call_stack, &mut r)?;
276
277 Ok(Value::new(r))
278 }
279
280 PythonResource::PackageDistributionResource(resource) => {
281 let mut r = PythonPackageDistributionResourceValue::new(resource.clone().into_owned());
282 policy.apply_to_resource(label, type_values, call_stack, &mut r)?;
283
284 Ok(Value::new(r))
285 }
286
287 PythonResource::ExtensionModule(em) => {
288 let mut em = PythonExtensionModuleValue::new(em.clone().into_owned());
289 policy.apply_to_resource(label, type_values, call_stack, &mut em)?;
290
291 Ok(Value::new(em))
292 }
293
294 PythonResource::File(f) => {
295 let mut value = FileValue::new(f.clone().into_owned());
296 policy.apply_to_resource(label, type_values, call_stack, &mut value)?;
297
298 Ok(Value::new(value))
299 }
300
301 _ => {
302 panic!("incompatible PythonResource variant passed; did you forget to filter through is_resource_starlark_compatible()?")
303 }
304 }
305}
306
307pub fn add_context_for_value(
309 value: &Value,
310 label: &str,
311) -> Result<Option<PythonResourceAddCollectionContext>, ValueError> {
312 match value.get_type() {
313 "PythonModuleSource" => Ok(value
314 .downcast_ref::<PythonModuleSourceValue>()
315 .unwrap()
316 .add_collection_context()?),
317 "PythonPackageResource" => Ok(value
318 .downcast_ref::<PythonPackageResourceValue>()
319 .unwrap()
320 .add_collection_context()?),
321 "PythonPackageDistributionResource" => Ok(value
322 .downcast_ref::<PythonPackageDistributionResourceValue>()
323 .unwrap()
324 .add_collection_context()?),
325 "PythonExtensionModule" => Ok(value
326 .downcast_ref::<PythonExtensionModuleValue>()
327 .unwrap()
328 .add_collection_context()?),
329 "File" => Ok(value
330 .downcast_ref::<FileValue>()
331 .unwrap()
332 .add_collection_context()?),
333 t => Err(ValueError::from(RuntimeError {
334 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
335 message: format!("unable to obtain add collection context from {}", t),
336 label: label.to_string(),
337 })),
338 }
339}