nbuild_core/models/cargo/
mod.rs

1//! Models used to read in cargo metadata. It is also used to determine which optional dependencies to enable, and
2//! which features to enable.
3
4use std::{
5    cell::RefCell,
6    collections::{BTreeMap, HashMap, HashSet},
7    path::PathBuf,
8    rc::Rc,
9};
10
11use cargo_lock::{Lockfile, Version};
12use cargo_metadata::{camino::Utf8PathBuf, DependencyKind, MetadataCommand, PackageId};
13use target_spec::{Platform, TargetSpec};
14use tracing::{instrument, trace};
15
16use crate::Error;
17
18use super::Source;
19
20mod visitor;
21
22pub use visitor::Visitor;
23
24/// Details of a package / crate
25#[derive(Debug, PartialEq, Clone)]
26pub struct Package {
27    pub(super) name: String,
28    pub(super) version: Version,
29    pub(super) source: Source,
30    pub(super) lib_name: Option<String>,
31    pub(super) lib_path: Option<Utf8PathBuf>,
32    pub(super) build_path: Option<Utf8PathBuf>,
33    pub(super) proc_macro: bool,
34
35    /// List of possible features for a package
36    pub(super) features: HashMap<String, Vec<String>>,
37
38    /// List of features that has been enabled
39    pub(super) enabled_features: HashSet<String>,
40    pub(super) dependencies: Vec<Dependency>,
41    pub(super) build_dependencies: Vec<Dependency>,
42    pub(super) edition: String,
43}
44
45/// A dependency of a package. This model is used to keep track of [renames][rename], [optional][optional] dependencies,
46/// [enabled features][features], and whether [`default-features`][default] is active or not.
47///
48/// [rename]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml
49/// [optional]: https://doc.rust-lang.org/cargo/reference/features.html#optional-dependencies
50/// [features]: https://doc.rust-lang.org/cargo/reference/features.html#dependency-features
51/// [default]: https://doc.rust-lang.org/cargo/reference/features.html#dependency-features
52#[derive(Debug, PartialEq, Clone)]
53pub struct Dependency {
54    pub(super) name: String,
55    pub(super) package: Rc<RefCell<Package>>,
56    pub(super) optional: bool,
57    pub(super) uses_default_features: bool,
58    pub(super) features: Vec<String>,
59}
60
61impl Package {
62    /// Get a package from a path with a `Cargo.toml` file
63    pub fn from_current_dir(path: impl Into<PathBuf>) -> Result<Self, Error> {
64        let platform = Platform::current()?;
65
66        let metadata = MetadataCommand::new()
67            .current_dir(path)
68            .other_options(vec![
69                "--filter-platform".to_string(),
70                platform.triple_str().to_string(),
71            ])
72            .exec()?;
73        let lock_file = metadata.workspace_root.join("Cargo.lock");
74        let lock_file = Lockfile::load(lock_file)?;
75
76        trace!(?platform, ?metadata, ?lock_file, "have metadata");
77
78        let packages =
79            BTreeMap::from_iter(metadata.packages.iter().map(|p| (p.id.clone(), p.clone())));
80        let nodes = BTreeMap::from_iter(
81            metadata
82                .resolve
83                .as_ref()
84                .expect("metadata to have a resolve section")
85                .nodes
86                .iter()
87                .map(|n| (n.id.clone(), n.clone())),
88        );
89        let checksums = BTreeMap::from_iter(lock_file.packages.iter().filter_map(|p| {
90            p.checksum.as_ref().map(|checksum| {
91                (
92                    (p.name.to_string(), p.version.to_string()),
93                    checksum.to_string(),
94                )
95            })
96        }));
97
98        let root_id = metadata
99            .resolve
100            .as_ref()
101            .expect("metadata to have a resolve section")
102            .root
103            .as_ref()
104            .expect("a root from metadata")
105            .clone();
106
107        let mut resolved_packages = Default::default();
108
109        Ok(Self::get_package(
110            root_id,
111            &packages,
112            &nodes,
113            &checksums,
114            &mut resolved_packages,
115            &platform,
116        ))
117    }
118
119    /// Recursively get a package and its dependencies. Use the `resolved_packages` to make sure we only
120    /// have one reverence to re-occuring packages.
121    fn get_package(
122        id: PackageId,
123        packages: &BTreeMap<PackageId, cargo_metadata::Package>,
124        nodes: &BTreeMap<PackageId, cargo_metadata::Node>,
125        checksums: &BTreeMap<(String, String), String>,
126        resolved_packages: &mut BTreeMap<PackageId, Rc<RefCell<Package>>>,
127        platform: &Platform,
128    ) -> Self {
129        let node = nodes.get(&id).expect("node to exist").clone();
130        let package = packages.get(&id).expect("package to exist");
131
132        trace!(
133            package.name,
134            ?package.features,
135            ?node,
136            "found package and node"
137        );
138
139        let features = package.features.clone();
140        let package_dependencies: Vec<_> = package
141            .dependencies
142            .iter()
143            .filter(|d| d.kind == DependencyKind::Normal)
144            .cloned()
145            .collect();
146        let package_build_dependencies: Vec<_> = package
147            .dependencies
148            .iter()
149            .filter(|d| d.kind == DependencyKind::Build)
150            .cloned()
151            .collect();
152
153        let dependencies = node
154            .dependencies
155            .iter()
156            .filter_map(|id| {
157                Dependency::get_dependency(
158                    id,
159                    &package_dependencies,
160                    packages,
161                    nodes,
162                    checksums,
163                    resolved_packages,
164                    platform,
165                )
166            })
167            .collect();
168        let build_dependencies = node
169            .dependencies
170            .iter()
171            .filter_map(|id| {
172                Dependency::get_dependency(
173                    id,
174                    &package_build_dependencies,
175                    packages,
176                    nodes,
177                    checksums,
178                    resolved_packages,
179                    platform,
180                )
181            })
182            .collect();
183
184        // Safe to unwrap since the manifest has to be in some directory
185        let package_path: PathBuf = package.manifest_path.parent().unwrap().into();
186
187        let (lib_path, lib_name) = package
188            .targets
189            .iter()
190            .find(|t| {
191                t.kind.iter().any(|k| {
192                    matches!(
193                        k.as_str(),
194                        "lib" | "cdylib" | "dylib" | "rlib" | "proc-macro"
195                    )
196                })
197            })
198            .map(|t| {
199                (
200                    t.src_path
201                        .strip_prefix(&package_path)
202                        .unwrap() // Safe to unwrap since the src has to be in the package path
203                        .to_path_buf(),
204                    t.name.clone(),
205                )
206            })
207            .unzip();
208        let build_path = package
209            .targets
210            .iter()
211            .find(|t| t.kind.iter().any(|k| k == "custom-build"))
212            .map(|t| {
213                t.src_path
214                    .strip_prefix(&package_path)
215                    .unwrap() // Safe to unwrap since the src has to be in the package path
216                    .to_path_buf()
217            });
218        let proc_macro = package
219            .targets
220            .iter()
221            .any(|t| t.kind.iter().any(|k| k == "proc-macro"));
222
223        let source = if package.source.is_some() {
224            let checksum = checksums
225                .get(&(package.name.to_string(), package.version.to_string()))
226                .expect("to have a checksum");
227            Source::CratesIo(checksum.to_string())
228        } else {
229            Source::Local(package_path)
230        };
231
232        Self {
233            name: package.name.clone(),
234            version: package.version.clone(),
235            source,
236            lib_name,
237            lib_path,
238            build_path,
239            proc_macro,
240            dependencies,
241            build_dependencies,
242            features,
243            enabled_features: Default::default(),
244            edition: package.edition.to_string(),
245        }
246    }
247
248    /// Resolve all the optional dependencies and enabled features of a package. This is done recursively and only
249    /// needed on the top level package.
250    pub fn resolve(&mut self) {
251        self.visit(&mut visitor::ResolveVisitor);
252    }
253
254    /// Helper to call visitor easier.
255    fn visit(&mut self, visitor: &mut impl visitor::Visitor) {
256        visitor.visit(self);
257    }
258
259    /// Get an iter for all the dependencies of a package. This is both normal dependencies and build dependencies.
260    pub fn dependencies_iter(&self) -> impl Iterator<Item = &Dependency> {
261        self.dependencies
262            .iter()
263            .chain(self.build_dependencies.iter())
264    }
265
266    /// Get a mutable iter for all the dependencies of a package. This is both normal dependencies and build dependencies.
267    pub fn dependencies_iter_mut(&mut self) -> impl Iterator<Item = &mut Dependency> {
268        self.dependencies
269            .iter_mut()
270            .chain(self.build_dependencies.iter_mut())
271    }
272}
273
274impl Dependency {
275    /// Recursively get a dependency and its package. Use the `resolved_packages` to make sure we only
276    /// have one reverence to re-occuring packages - this is needed during feature resolution
277    #[instrument(skip_all, fields(%id))]
278    fn get_dependency(
279        id: &PackageId,
280        parent_dependencies: &[cargo_metadata::Dependency],
281        packages: &BTreeMap<PackageId, cargo_metadata::Package>,
282        nodes: &BTreeMap<PackageId, cargo_metadata::Node>,
283        checksums: &BTreeMap<(String, String), String>,
284        resolved_packages: &mut BTreeMap<PackageId, Rc<RefCell<Package>>>,
285        platform: &Platform,
286    ) -> Option<Self> {
287        let package = match resolved_packages.get(id) {
288            Some(package) => Rc::clone(package),
289            None => {
290                let package = RefCell::new(Package::get_package(
291                    id.clone(),
292                    packages,
293                    nodes,
294                    checksums,
295                    resolved_packages,
296                    platform,
297                ))
298                .into();
299
300                resolved_packages.insert(id.clone(), Rc::clone(&package));
301
302                package
303            }
304        };
305
306        // Handle renames
307        let name = package.borrow().name.clone();
308        let version = package.borrow().version.clone();
309
310        // A dependency may appear more than once because of targets. So only get those that match the current target.
311        //
312        // https://doc.rust-lang.org/cargo/reference/config.html#target
313        let dependencies: Vec<_> = parent_dependencies
314            .iter()
315            .filter(|d| d.name == name)
316            .filter(|d| d.req.matches(&version))
317            .filter(|d| match &d.target {
318                Some(target_spec) => {
319                    // Safe to unwrap since cargo would have failed if the target spec was not valid
320                    let target_spec = TargetSpec::new(target_spec.to_string()).unwrap();
321
322                    target_spec.eval(platform).unwrap_or(false)
323                }
324                None => true,
325            })
326            .collect();
327
328        // It could happen that this kind of dependency is not part of the kind passed into this function,
329        // in which case this dependency should not we considered as a real dependency.
330        if dependencies.is_empty() {
331            return None;
332        }
333
334        // Start with sane default assumptions
335        let mut optional = true;
336        let mut uses_default_features = false;
337        let mut features: Vec<String> = Default::default();
338        let mut dependency_name: String = Default::default();
339        let mut dependency_rename = None;
340
341        for dependency in dependencies {
342            if !dependency.optional {
343                optional = false;
344            }
345
346            if dependency.uses_default_features {
347                uses_default_features = true;
348            }
349
350            // Features should be additive
351            features.extend(dependency.features.iter().cloned());
352
353            if dependency_rename.is_none() && dependency.rename.is_some() {
354                dependency_rename = dependency.rename.clone();
355            }
356
357            if dependency_name.is_empty() {
358                dependency_name = dependency.name.clone();
359            }
360        }
361
362        if let Some(dependency_rename) = dependency_rename {
363            dependency_name = dependency_rename;
364        };
365
366        trace!(
367            name,
368            dependency_name,
369            optional,
370            uses_default_features,
371            ?features,
372            "done with dependency"
373        );
374
375        Some(Self {
376            name: dependency_name,
377            package,
378            optional,
379            uses_default_features,
380            features,
381        })
382    }
383}
384
385#[cfg(test)]
386mod tests {
387    use std::{cell::RefCell, collections::HashMap, path::PathBuf, str::FromStr};
388
389    use crate::models::cargo::{Dependency, Package};
390
391    use pretty_assertions::assert_eq;
392
393    #[test]
394    fn simple_package() {
395        let path = PathBuf::from_str(env!("CARGO_MANIFEST_DIR"))
396            .unwrap()
397            .join("tests")
398            .join("simple");
399
400        let package = Package::from_current_dir(path.clone()).unwrap();
401
402        assert_eq!(
403            package,
404            Package {
405                name: "simple".to_string(),
406                source: path.into(),
407                lib_name: None,
408                lib_path: None,
409                build_path: None,
410                proc_macro: false,
411                version: "0.1.0".parse().unwrap(),
412                dependencies: vec![Dependency {
413                    name: "itoa".to_string(),
414                    package: RefCell::new(Package {
415                        name: "itoa".to_string(),
416                        version: "1.0.6".parse().unwrap(),
417                        source: "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
418                            .into(),
419                        lib_name: Some("itoa".to_string()),
420                        lib_path: Some("src/lib.rs".into()),
421                        build_path: None,
422                        proc_macro: false,
423                        dependencies: Default::default(),
424                        build_dependencies: Default::default(),
425                        features: HashMap::from([(
426                            "no-panic".to_string(),
427                            vec!["dep:no-panic".to_string()]
428                        )]),
429                        enabled_features: Default::default(),
430                        edition: "2018".to_string(),
431                    })
432                    .into(),
433                    optional: false,
434                    uses_default_features: true,
435                    features: Default::default(),
436                },],
437                build_dependencies: vec![Dependency {
438                    name: "arbitrary".to_string(),
439                    package: RefCell::new(Package {
440                        name: "arbitrary".to_string(),
441                        version: "1.3.0".parse().unwrap(),
442                        source: "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
443                            .into(),
444                        lib_name: Some("arbitrary".to_string()),
445                        lib_path: Some("src/lib.rs".into()),
446                        build_path: None,
447                        proc_macro: false,
448                        dependencies: Default::default(),
449                        build_dependencies: Default::default(),
450                        features: HashMap::from([
451                            ("derive".to_string(), vec!["derive_arbitrary".to_string()]),
452                            (
453                                "derive_arbitrary".to_string(),
454                                vec!["dep:derive_arbitrary".to_string()]
455                            ),
456                        ]),
457                        enabled_features: Default::default(),
458                        edition: "2018".to_string(),
459                    })
460                    .into(),
461                    optional: false,
462                    uses_default_features: true,
463                    features: Default::default(),
464                },],
465                features: Default::default(),
466                enabled_features: Default::default(),
467                edition: "2021".to_string(),
468            }
469        );
470    }
471
472    #[test]
473    fn workspace() {
474        let workspace = PathBuf::from_str(env!("CARGO_MANIFEST_DIR"))
475            .unwrap()
476            .join("tests")
477            .join("workspace");
478        let path = workspace.join("parent");
479
480        let package = Package::from_current_dir(path.clone()).unwrap();
481
482        assert_eq!(
483            package,
484            Package {
485                name: "parent".to_string(),
486                version: "0.1.0".parse().unwrap(),
487                source: path.into(),
488                lib_name: None,
489                lib_path: None,
490                build_path: None,
491                proc_macro: false,
492                dependencies: vec![
493                    Dependency {
494                        name: "child".to_string(),
495                        package: RefCell::new(Package {
496                            name: "child".to_string(),
497                            version: "0.1.0".parse().unwrap(),
498                            source: workspace.join("child").into(),
499                            lib_name: Some("child".to_string()),
500                            lib_path: Some("src/lib.rs".into()),
501                            build_path: None,
502                            proc_macro: false,
503                            dependencies: vec![
504                                Dependency {
505                                    name: "fnv".to_string(),
506                                    package: RefCell::new(Package {
507                                        name: "fnv".to_string(),
508                                        version: "1.0.7".parse().unwrap(),
509                                        source: "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1".into(),
510                                        lib_name: Some("fnv".to_string()),
511                                        lib_path: Some("lib.rs".into()),
512                                        build_path: None,
513                                        proc_macro: false,
514                                        dependencies: Default::default(),
515                                        build_dependencies: Default::default(),
516                                        features: HashMap::from([
517                                            ("default".to_string(), vec!["std".to_string()]),
518                                            ("std".to_string(), vec![]),
519                                        ]),
520                                        enabled_features: Default::default(),
521                                        edition: "2015".to_string(),
522                                    })
523                                    .into(),
524                                    optional: false,
525                                    uses_default_features: true,
526                                    features: Default::default(),
527                                },
528                                Dependency {
529                                    name: "itoa".to_string(),
530                                    package: RefCell::new(Package {
531                                        name: "itoa".to_string(),
532                                        version: "1.0.6".parse().unwrap(),
533                                        source: "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6".into(),
534                                        lib_name: Some("itoa".to_string()),
535                                        lib_path: Some("src/lib.rs".into()),
536                                        build_path: None,
537                                        proc_macro: false,
538                                        dependencies: Default::default(),
539                                        build_dependencies: Default::default(),
540                                        features: HashMap::from([(
541                                            "no-panic".to_string(),
542                                            vec!["dep:no-panic".to_string()]
543                                        )]),
544                                        enabled_features: Default::default(),
545                                        edition: "2018".to_string(),
546                                    })
547                                    .into(),
548                                    optional: false,
549                                    uses_default_features: true,
550                                    features: Default::default(),
551                                },
552                                Dependency {
553                                    name: "libc".to_string(),
554                                    package: RefCell::new(Package {
555                                        name: "libc".to_string(),
556                                        version: "0.2.144".parse().unwrap(),
557                                        source: "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1".into(),
558                                        lib_name: Some("libc".to_string()),
559                                        lib_path: Some("src/lib.rs".into()),
560                                        build_path: Some("build.rs".into()),
561                                        proc_macro: false,
562                                        dependencies: Default::default(),
563                                        build_dependencies: Default::default(),
564                                        features: HashMap::from([
565                                            ("std".to_string(), vec![]),
566                                            ("default".to_string(), vec!["std".to_string()]),
567                                            ("use_std".to_string(), vec!["std".to_string()]),
568                                            ("extra_traits".to_string(), vec![]),
569                                            ("align".to_string(), vec![]),
570                                            (
571                                                "rustc-dep-of-std".to_string(),
572                                                vec![
573                                                    "align".to_string(),
574                                                    "rustc-std-workspace-core".to_string()
575                                                ]
576                                            ),
577                                            ("const-extern-fn".to_string(), vec![]),
578                                            (
579                                                "rustc-std-workspace-core".to_string(),
580                                                vec!["dep:rustc-std-workspace-core".to_string()]
581                                            ),
582                                        ]),
583                                        enabled_features: Default::default(),
584                                        edition: "2015".to_string(),
585                                    })
586                                    .into(),
587                                    optional: false,
588                                    uses_default_features: true,
589                                    features: Default::default(),
590                                },
591                                Dependency {
592                                    name: "new_name".to_string(),
593                                    package: RefCell::new(Package {
594                                        name: "rename".to_string(),
595                                        version: "0.1.0".parse().unwrap(),
596                                        source: workspace.join("rename").into(),
597                                        lib_name: Some("lib_rename".to_string()),
598                                        lib_path: Some("src/lib.rs".into()),
599                                        build_path: None,
600                                        proc_macro: false,
601                                        dependencies: Default::default(),
602                                        build_dependencies: Default::default(),
603                                        features: Default::default(),
604                                        enabled_features: Default::default(),
605                                        edition: "2021".to_string(),
606                                    })
607                                    .into(),
608                                    optional: true,
609                                    uses_default_features: true,
610                                    features: Default::default(),
611                                },
612                                Dependency {
613                                    name: "rustversion".to_string(),
614                                    package: RefCell::new(Package {
615                                        name: "rustversion".to_string(),
616                                        version: "1.0.12".parse().unwrap(),
617                                        source: "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06".into(),
618                                        lib_name: Some("rustversion".to_string()),
619                                        lib_path: Some("src/lib.rs".into()),
620                                        build_path: Some("build/build.rs".into()),
621                                        proc_macro: true,
622                                        dependencies: Default::default(),
623                                        build_dependencies: Default::default(),
624                                        features: Default::default(),
625                                        enabled_features: Default::default(),
626                                        edition: "2018".to_string(),
627                                    })
628                                    .into(),
629                                    optional: false,
630                                    uses_default_features: true,
631                                    features: Default::default(),
632                                },
633                            ],
634                            build_dependencies: Default::default(),
635                            features: HashMap::from([
636                                (
637                                    "default".to_string(),
638                                    vec!["one".to_string(), "two".to_string()]
639                                ),
640                                ("one".to_string(), vec!["new_name".to_string()]),
641                                ("two".to_string(), vec![]),
642                                ("new_name".to_string(), vec!["dep:new_name".to_string()]),
643                            ]),
644                            enabled_features: Default::default(),
645                            edition: "2021".to_string(),
646                        })
647                        .into(),
648                        optional: false,
649                        uses_default_features: false,
650                        features: vec!["one".to_string()],
651                    },
652                    Dependency {
653                        name: "itoa".to_string(),
654                        package: RefCell::new(Package {
655                            name: "itoa".to_string(),
656                            version: "0.4.8".parse().unwrap(),
657                            source: "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4".into(),
658                            lib_name: Some("itoa".to_string()),
659                            lib_path: Some("src/lib.rs".into()),
660                            build_path: None,
661                            proc_macro: false,
662                            dependencies: Default::default(),
663                            build_dependencies: Default::default(),
664                            features: HashMap::from([
665                                ("default".to_string(), vec!["std".to_string()]),
666                                ("std".to_string(), vec![]),
667                                ("i128".to_string(), vec![]),
668                            ]),
669                            enabled_features: Default::default(),
670                            edition: "2015".to_string(),
671                        })
672                        .into(),
673                        optional: false,
674                        uses_default_features: true,
675                        features: Default::default(),
676                    },
677                    Dependency {
678                        name: "libc".to_string(),
679                        package: RefCell::new(Package {
680                            name: "libc".to_string(),
681                            version: "0.2.144".parse().unwrap(),
682                            source: "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1".into(),
683                            lib_name: Some("libc".to_string()),
684                            lib_path: Some("src/lib.rs".into()),
685                            build_path: Some("build.rs".into()),
686                            proc_macro: false,
687                            dependencies: Default::default(),
688                            build_dependencies: Default::default(),
689                            features: HashMap::from([
690                                ("std".to_string(), vec![]),
691                                ("default".to_string(), vec!["std".to_string()]),
692                                ("use_std".to_string(), vec!["std".to_string()]),
693                                ("extra_traits".to_string(), vec![]),
694                                ("align".to_string(), vec![]),
695                                (
696                                    "rustc-dep-of-std".to_string(),
697                                    vec![
698                                        "align".to_string(),
699                                        "rustc-std-workspace-core".to_string()
700                                    ]
701                                ),
702                                ("const-extern-fn".to_string(), vec![]),
703                                (
704                                    "rustc-std-workspace-core".to_string(),
705                                    vec!["dep:rustc-std-workspace-core".to_string()]
706                                ),
707                            ]),
708                            enabled_features: Default::default(),
709                            edition: "2015".to_string(),
710                        })
711                        .into(),
712                        optional: false,
713                        uses_default_features: true,
714                        features: Default::default(),
715                    },
716                    Dependency {
717                        name: "targets".to_string(),
718                        package: RefCell::new(Package {
719                            name: "targets".to_string(),
720                            version: "0.1.0".parse().unwrap(),
721                            source: workspace.join("targets").into(),
722                            lib_name: Some("targets".to_string()),
723                            lib_path: Some("src/lib.rs".into()),
724                            build_path: None,
725                            proc_macro: false,
726                            dependencies: Default::default(),
727                            build_dependencies: Default::default(),
728                            features: HashMap::from([
729                                ("unix".to_string(), vec![]),
730                                ("windows".to_string(), vec![]),
731                            ]),
732                            enabled_features: Default::default(),
733                            edition: "2021".to_string(),
734                        })
735                        .into(),
736                        optional: false,
737                        uses_default_features: true,
738                        features: vec!["unix".to_string()],
739                    },
740                ],
741                build_dependencies: Default::default(),
742                features: Default::default(),
743                enabled_features: Default::default(),
744                edition: "2021".to_string(),
745            }
746        );
747    }
748}