diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-19 17:01:14 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-19 17:01:14 +0000 |
commit | 988f1dda6bde576ec2457dd97a7525014609c771 (patch) | |
tree | 52b5e578093db49ed1822f3b4b23d94fc963337c /crates/ra_project_model | |
parent | d013a05fee5bcf1dfff7bc3a273dd064b61bab91 (diff) | |
parent | fc230b943b1079556e6d844bc22790ffb969da9c (diff) |
Merge #3658
3658: More direct CargoWorkspace r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_project_model')
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 105 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 50 | ||||
-rw-r--r-- | crates/ra_project_model/src/sysroot.rs | 32 |
3 files changed, 81 insertions, 106 deletions
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 10ecfa951..43dbd096a 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::path::{Path, PathBuf}; | 3 | use std::{ |
4 | ops, | ||
5 | path::{Path, PathBuf}, | ||
6 | }; | ||
4 | 7 | ||
5 | use anyhow::{Context, Result}; | 8 | use anyhow::{Context, Result}; |
6 | use cargo_metadata::{CargoOpt, Message, MetadataCommand, PackageId}; | 9 | use cargo_metadata::{CargoOpt, Message, MetadataCommand, PackageId}; |
@@ -24,6 +27,20 @@ pub struct CargoWorkspace { | |||
24 | workspace_root: PathBuf, | 27 | workspace_root: PathBuf, |
25 | } | 28 | } |
26 | 29 | ||
30 | impl ops::Index<Package> for CargoWorkspace { | ||
31 | type Output = PackageData; | ||
32 | fn index(&self, index: Package) -> &PackageData { | ||
33 | &self.packages[index] | ||
34 | } | ||
35 | } | ||
36 | |||
37 | impl ops::Index<Target> for CargoWorkspace { | ||
38 | type Output = TargetData; | ||
39 | fn index(&self, index: Target) -> &TargetData { | ||
40 | &self.targets[index] | ||
41 | } | ||
42 | } | ||
43 | |||
27 | #[derive(Deserialize, Clone, Debug, PartialEq, Eq)] | 44 | #[derive(Deserialize, Clone, Debug, PartialEq, Eq)] |
28 | #[serde(rename_all = "camelCase", default)] | 45 | #[serde(rename_all = "camelCase", default)] |
29 | pub struct CargoFeatures { | 46 | pub struct CargoFeatures { |
@@ -61,15 +78,15 @@ pub struct Target(RawId); | |||
61 | impl_arena_id!(Target); | 78 | impl_arena_id!(Target); |
62 | 79 | ||
63 | #[derive(Debug, Clone)] | 80 | #[derive(Debug, Clone)] |
64 | struct PackageData { | 81 | pub struct PackageData { |
65 | name: String, | 82 | pub name: String, |
66 | manifest: PathBuf, | 83 | pub manifest: PathBuf, |
67 | targets: Vec<Target>, | 84 | pub targets: Vec<Target>, |
68 | is_member: bool, | 85 | pub is_member: bool, |
69 | dependencies: Vec<PackageDependency>, | 86 | pub dependencies: Vec<PackageDependency>, |
70 | edition: Edition, | 87 | pub edition: Edition, |
71 | features: Vec<String>, | 88 | pub features: Vec<String>, |
72 | out_dir: Option<PathBuf>, | 89 | pub out_dir: Option<PathBuf>, |
73 | } | 90 | } |
74 | 91 | ||
75 | #[derive(Debug, Clone)] | 92 | #[derive(Debug, Clone)] |
@@ -79,12 +96,12 @@ pub struct PackageDependency { | |||
79 | } | 96 | } |
80 | 97 | ||
81 | #[derive(Debug, Clone)] | 98 | #[derive(Debug, Clone)] |
82 | struct TargetData { | 99 | pub struct TargetData { |
83 | pkg: Package, | 100 | pub package: Package, |
84 | name: String, | 101 | pub name: String, |
85 | root: PathBuf, | 102 | pub root: PathBuf, |
86 | kind: TargetKind, | 103 | pub kind: TargetKind, |
87 | is_proc_macro: bool, | 104 | pub is_proc_macro: bool, |
88 | } | 105 | } |
89 | 106 | ||
90 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 107 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
@@ -115,52 +132,9 @@ impl TargetKind { | |||
115 | } | 132 | } |
116 | } | 133 | } |
117 | 134 | ||
118 | impl Package { | 135 | impl PackageData { |
119 | pub fn name(self, ws: &CargoWorkspace) -> &str { | 136 | pub fn root(&self) -> &Path { |
120 | ws.packages[self].name.as_str() | 137 | self.manifest.parent().unwrap() |
121 | } | ||
122 | pub fn root(self, ws: &CargoWorkspace) -> &Path { | ||
123 | ws.packages[self].manifest.parent().unwrap() | ||
124 | } | ||
125 | pub fn edition(self, ws: &CargoWorkspace) -> Edition { | ||
126 | ws.packages[self].edition | ||
127 | } | ||
128 | pub fn features(self, ws: &CargoWorkspace) -> &[String] { | ||
129 | &ws.packages[self].features | ||
130 | } | ||
131 | pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a { | ||
132 | ws.packages[self].targets.iter().cloned() | ||
133 | } | ||
134 | #[allow(unused)] | ||
135 | pub fn is_member(self, ws: &CargoWorkspace) -> bool { | ||
136 | ws.packages[self].is_member | ||
137 | } | ||
138 | pub fn dependencies<'a>( | ||
139 | self, | ||
140 | ws: &'a CargoWorkspace, | ||
141 | ) -> impl Iterator<Item = &'a PackageDependency> + 'a { | ||
142 | ws.packages[self].dependencies.iter() | ||
143 | } | ||
144 | pub fn out_dir(self, ws: &CargoWorkspace) -> Option<&Path> { | ||
145 | ws.packages[self].out_dir.as_ref().map(PathBuf::as_path) | ||
146 | } | ||
147 | } | ||
148 | |||
149 | impl Target { | ||
150 | pub fn package(self, ws: &CargoWorkspace) -> Package { | ||
151 | ws.targets[self].pkg | ||
152 | } | ||
153 | pub fn name(self, ws: &CargoWorkspace) -> &str { | ||
154 | ws.targets[self].name.as_str() | ||
155 | } | ||
156 | pub fn root(self, ws: &CargoWorkspace) -> &Path { | ||
157 | ws.targets[self].root.as_path() | ||
158 | } | ||
159 | pub fn kind(self, ws: &CargoWorkspace) -> TargetKind { | ||
160 | ws.targets[self].kind | ||
161 | } | ||
162 | pub fn is_proc_macro(self, ws: &CargoWorkspace) -> bool { | ||
163 | ws.targets[self].is_proc_macro | ||
164 | } | 138 | } |
165 | } | 139 | } |
166 | 140 | ||
@@ -219,7 +193,7 @@ impl CargoWorkspace { | |||
219 | for meta_tgt in meta_pkg.targets { | 193 | for meta_tgt in meta_pkg.targets { |
220 | let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"]; | 194 | let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"]; |
221 | let tgt = targets.alloc(TargetData { | 195 | let tgt = targets.alloc(TargetData { |
222 | pkg, | 196 | package: pkg, |
223 | name: meta_tgt.name, | 197 | name: meta_tgt.name, |
224 | root: meta_tgt.src_path.clone(), | 198 | root: meta_tgt.src_path.clone(), |
225 | kind: TargetKind::new(meta_tgt.kind.as_slice()), | 199 | kind: TargetKind::new(meta_tgt.kind.as_slice()), |
@@ -265,7 +239,10 @@ impl CargoWorkspace { | |||
265 | } | 239 | } |
266 | 240 | ||
267 | pub fn target_by_root(&self, root: &Path) -> Option<Target> { | 241 | pub fn target_by_root(&self, root: &Path) -> Option<Target> { |
268 | self.packages().filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)).next() | 242 | self.packages() |
243 | .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root)) | ||
244 | .next() | ||
245 | .copied() | ||
269 | } | 246 | } |
270 | 247 | ||
271 | pub fn workspace_root(&self) -> &Path { | 248 | pub fn workspace_root(&self) -> &Path { |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 081b1fec2..b500a74fb 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -138,12 +138,12 @@ impl ProjectWorkspace { | |||
138 | ProjectWorkspace::Cargo { cargo, sysroot } => { | 138 | ProjectWorkspace::Cargo { cargo, sysroot } => { |
139 | let mut roots = Vec::with_capacity(cargo.packages().len() + sysroot.crates().len()); | 139 | let mut roots = Vec::with_capacity(cargo.packages().len() + sysroot.crates().len()); |
140 | for pkg in cargo.packages() { | 140 | for pkg in cargo.packages() { |
141 | let root = pkg.root(&cargo).to_path_buf(); | 141 | let root = cargo[pkg].root().to_path_buf(); |
142 | let member = pkg.is_member(&cargo); | 142 | let member = cargo[pkg].is_member; |
143 | roots.push(PackageRoot::new(root, member)); | 143 | roots.push(PackageRoot::new(root, member)); |
144 | } | 144 | } |
145 | for krate in sysroot.crates() { | 145 | for krate in sysroot.crates() { |
146 | roots.push(PackageRoot::new(krate.root_dir(&sysroot).to_path_buf(), false)) | 146 | roots.push(PackageRoot::new(sysroot[krate].root_dir().to_path_buf(), false)) |
147 | } | 147 | } |
148 | roots | 148 | roots |
149 | } | 149 | } |
@@ -164,7 +164,7 @@ impl ProjectWorkspace { | |||
164 | ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => { | 164 | ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => { |
165 | let mut out_dirs = Vec::with_capacity(cargo.packages().len()); | 165 | let mut out_dirs = Vec::with_capacity(cargo.packages().len()); |
166 | for pkg in cargo.packages() { | 166 | for pkg in cargo.packages() { |
167 | if let Some(out_dir) = pkg.out_dir(&cargo) { | 167 | if let Some(out_dir) = &cargo[pkg].out_dir { |
168 | out_dirs.push(out_dir.to_path_buf()); | 168 | out_dirs.push(out_dir.to_path_buf()); |
169 | } | 169 | } |
170 | } | 170 | } |
@@ -260,7 +260,7 @@ impl ProjectWorkspace { | |||
260 | ProjectWorkspace::Cargo { cargo, sysroot } => { | 260 | ProjectWorkspace::Cargo { cargo, sysroot } => { |
261 | let mut sysroot_crates = FxHashMap::default(); | 261 | let mut sysroot_crates = FxHashMap::default(); |
262 | for krate in sysroot.crates() { | 262 | for krate in sysroot.crates() { |
263 | if let Some(file_id) = load(krate.root(&sysroot)) { | 263 | if let Some(file_id) = load(&sysroot[krate].root) { |
264 | // Crates from sysroot have `cfg(test)` disabled | 264 | // Crates from sysroot have `cfg(test)` disabled |
265 | let cfg_options = { | 265 | let cfg_options = { |
266 | let mut opts = default_cfg_options.clone(); | 266 | let mut opts = default_cfg_options.clone(); |
@@ -274,7 +274,7 @@ impl ProjectWorkspace { | |||
274 | file_id, | 274 | file_id, |
275 | Edition::Edition2018, | 275 | Edition::Edition2018, |
276 | Some( | 276 | Some( |
277 | CrateName::new(krate.name(&sysroot)) | 277 | CrateName::new(&sysroot[krate].name) |
278 | .expect("Sysroot crate names should not contain dashes"), | 278 | .expect("Sysroot crate names should not contain dashes"), |
279 | ), | 279 | ), |
280 | cfg_options, | 280 | cfg_options, |
@@ -285,8 +285,8 @@ impl ProjectWorkspace { | |||
285 | } | 285 | } |
286 | } | 286 | } |
287 | for from in sysroot.crates() { | 287 | for from in sysroot.crates() { |
288 | for to in from.deps(&sysroot) { | 288 | for &to in sysroot[from].deps.iter() { |
289 | let name = to.name(&sysroot); | 289 | let name = &sysroot[to].name; |
290 | if let (Some(&from), Some(&to)) = | 290 | if let (Some(&from), Some(&to)) = |
291 | (sysroot_crates.get(&from), sysroot_crates.get(&to)) | 291 | (sysroot_crates.get(&from), sysroot_crates.get(&to)) |
292 | { | 292 | { |
@@ -309,18 +309,18 @@ impl ProjectWorkspace { | |||
309 | // Next, create crates for each package, target pair | 309 | // Next, create crates for each package, target pair |
310 | for pkg in cargo.packages() { | 310 | for pkg in cargo.packages() { |
311 | let mut lib_tgt = None; | 311 | let mut lib_tgt = None; |
312 | for tgt in pkg.targets(&cargo) { | 312 | for &tgt in cargo[pkg].targets.iter() { |
313 | let root = tgt.root(&cargo); | 313 | let root = cargo[tgt].root.as_path(); |
314 | if let Some(file_id) = load(root) { | 314 | if let Some(file_id) = load(root) { |
315 | let edition = pkg.edition(&cargo); | 315 | let edition = cargo[pkg].edition; |
316 | let cfg_options = { | 316 | let cfg_options = { |
317 | let mut opts = default_cfg_options.clone(); | 317 | let mut opts = default_cfg_options.clone(); |
318 | opts.insert_features(pkg.features(&cargo).iter().map(Into::into)); | 318 | opts.insert_features(cargo[pkg].features.iter().map(Into::into)); |
319 | opts | 319 | opts |
320 | }; | 320 | }; |
321 | let mut env = Env::default(); | 321 | let mut env = Env::default(); |
322 | let mut extern_source = ExternSource::default(); | 322 | let mut extern_source = ExternSource::default(); |
323 | if let Some(out_dir) = pkg.out_dir(cargo) { | 323 | if let Some(out_dir) = &cargo[pkg].out_dir { |
324 | // FIXME: We probably mangle non UTF-8 paths here, figure out a better solution | 324 | // FIXME: We probably mangle non UTF-8 paths here, figure out a better solution |
325 | env.set("OUT_DIR", out_dir.to_string_lossy().to_string()); | 325 | env.set("OUT_DIR", out_dir.to_string_lossy().to_string()); |
326 | if let Some(&extern_source_id) = extern_source_roots.get(out_dir) { | 326 | if let Some(&extern_source_id) = extern_source_roots.get(out_dir) { |
@@ -330,16 +330,16 @@ impl ProjectWorkspace { | |||
330 | let crate_id = crate_graph.add_crate_root( | 330 | let crate_id = crate_graph.add_crate_root( |
331 | file_id, | 331 | file_id, |
332 | edition, | 332 | edition, |
333 | Some(CrateName::normalize_dashes(pkg.name(&cargo))), | 333 | Some(CrateName::normalize_dashes(&cargo[pkg].name)), |
334 | cfg_options, | 334 | cfg_options, |
335 | env, | 335 | env, |
336 | extern_source, | 336 | extern_source, |
337 | ); | 337 | ); |
338 | if tgt.kind(&cargo) == TargetKind::Lib { | 338 | if cargo[tgt].kind == TargetKind::Lib { |
339 | lib_tgt = Some(crate_id); | 339 | lib_tgt = Some(crate_id); |
340 | pkg_to_lib_crate.insert(pkg, crate_id); | 340 | pkg_to_lib_crate.insert(pkg, crate_id); |
341 | } | 341 | } |
342 | if tgt.is_proc_macro(&cargo) { | 342 | if cargo[tgt].is_proc_macro { |
343 | if let Some(proc_macro) = libproc_macro { | 343 | if let Some(proc_macro) = libproc_macro { |
344 | if crate_graph | 344 | if crate_graph |
345 | .add_dep( | 345 | .add_dep( |
@@ -351,7 +351,7 @@ impl ProjectWorkspace { | |||
351 | { | 351 | { |
352 | log::error!( | 352 | log::error!( |
353 | "cyclic dependency on proc_macro for {}", | 353 | "cyclic dependency on proc_macro for {}", |
354 | pkg.name(&cargo) | 354 | &cargo[pkg].name |
355 | ) | 355 | ) |
356 | } | 356 | } |
357 | } | 357 | } |
@@ -371,7 +371,7 @@ impl ProjectWorkspace { | |||
371 | // For root projects with dashes in their name, | 371 | // For root projects with dashes in their name, |
372 | // cargo metadata does not do any normalization, | 372 | // cargo metadata does not do any normalization, |
373 | // so we do it ourselves currently | 373 | // so we do it ourselves currently |
374 | CrateName::normalize_dashes(pkg.name(&cargo)), | 374 | CrateName::normalize_dashes(&cargo[pkg].name), |
375 | to, | 375 | to, |
376 | ) | 376 | ) |
377 | .is_err() | 377 | .is_err() |
@@ -379,7 +379,7 @@ impl ProjectWorkspace { | |||
379 | { | 379 | { |
380 | log::error!( | 380 | log::error!( |
381 | "cyclic dependency between targets of {}", | 381 | "cyclic dependency between targets of {}", |
382 | pkg.name(&cargo) | 382 | &cargo[pkg].name |
383 | ) | 383 | ) |
384 | } | 384 | } |
385 | } | 385 | } |
@@ -391,7 +391,7 @@ impl ProjectWorkspace { | |||
391 | .add_dep(from, CrateName::new("core").unwrap(), core) | 391 | .add_dep(from, CrateName::new("core").unwrap(), core) |
392 | .is_err() | 392 | .is_err() |
393 | { | 393 | { |
394 | log::error!("cyclic dependency on core for {}", pkg.name(&cargo)) | 394 | log::error!("cyclic dependency on core for {}", &cargo[pkg].name) |
395 | } | 395 | } |
396 | } | 396 | } |
397 | if let Some(alloc) = liballoc { | 397 | if let Some(alloc) = liballoc { |
@@ -399,7 +399,7 @@ impl ProjectWorkspace { | |||
399 | .add_dep(from, CrateName::new("alloc").unwrap(), alloc) | 399 | .add_dep(from, CrateName::new("alloc").unwrap(), alloc) |
400 | .is_err() | 400 | .is_err() |
401 | { | 401 | { |
402 | log::error!("cyclic dependency on alloc for {}", pkg.name(&cargo)) | 402 | log::error!("cyclic dependency on alloc for {}", &cargo[pkg].name) |
403 | } | 403 | } |
404 | } | 404 | } |
405 | if let Some(std) = libstd { | 405 | if let Some(std) = libstd { |
@@ -407,7 +407,7 @@ impl ProjectWorkspace { | |||
407 | .add_dep(from, CrateName::new("std").unwrap(), std) | 407 | .add_dep(from, CrateName::new("std").unwrap(), std) |
408 | .is_err() | 408 | .is_err() |
409 | { | 409 | { |
410 | log::error!("cyclic dependency on std for {}", pkg.name(&cargo)) | 410 | log::error!("cyclic dependency on std for {}", &cargo[pkg].name) |
411 | } | 411 | } |
412 | } | 412 | } |
413 | } | 413 | } |
@@ -416,7 +416,7 @@ impl ProjectWorkspace { | |||
416 | // Now add a dep edge from all targets of upstream to the lib | 416 | // Now add a dep edge from all targets of upstream to the lib |
417 | // target of downstream. | 417 | // target of downstream. |
418 | for pkg in cargo.packages() { | 418 | for pkg in cargo.packages() { |
419 | for dep in pkg.dependencies(&cargo) { | 419 | for dep in cargo[pkg].dependencies.iter() { |
420 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | 420 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { |
421 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 421 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { |
422 | if crate_graph | 422 | if crate_graph |
@@ -425,8 +425,8 @@ impl ProjectWorkspace { | |||
425 | { | 425 | { |
426 | log::error!( | 426 | log::error!( |
427 | "cyclic dependency {} -> {}", | 427 | "cyclic dependency {} -> {}", |
428 | pkg.name(&cargo), | 428 | &cargo[pkg].name, |
429 | dep.pkg.name(&cargo) | 429 | &cargo[dep.pkg].name |
430 | ) | 430 | ) |
431 | } | 431 | } |
432 | } | 432 | } |
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index c51957c1f..4ac4fa14d 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use anyhow::{bail, Context, Result}; | 3 | use anyhow::{bail, Context, Result}; |
4 | use std::{ | 4 | use std::{ |
5 | env, | 5 | env, ops, |
6 | path::{Path, PathBuf}, | 6 | path::{Path, PathBuf}, |
7 | process::{Command, Output}, | 7 | process::{Command, Output}, |
8 | }; | 8 | }; |
@@ -19,10 +19,17 @@ pub struct SysrootCrate(RawId); | |||
19 | impl_arena_id!(SysrootCrate); | 19 | impl_arena_id!(SysrootCrate); |
20 | 20 | ||
21 | #[derive(Debug, Clone)] | 21 | #[derive(Debug, Clone)] |
22 | struct SysrootCrateData { | 22 | pub struct SysrootCrateData { |
23 | name: String, | 23 | pub name: String, |
24 | root: PathBuf, | 24 | pub root: PathBuf, |
25 | deps: Vec<SysrootCrate>, | 25 | pub deps: Vec<SysrootCrate>, |
26 | } | ||
27 | |||
28 | impl ops::Index<SysrootCrate> for Sysroot { | ||
29 | type Output = SysrootCrateData; | ||
30 | fn index(&self, index: SysrootCrate) -> &SysrootCrateData { | ||
31 | &self.crates[index] | ||
32 | } | ||
26 | } | 33 | } |
27 | 34 | ||
28 | impl Sysroot { | 35 | impl Sysroot { |
@@ -129,18 +136,9 @@ fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> { | |||
129 | Ok(src_path) | 136 | Ok(src_path) |
130 | } | 137 | } |
131 | 138 | ||
132 | impl SysrootCrate { | 139 | impl SysrootCrateData { |
133 | pub fn name(self, sysroot: &Sysroot) -> &str { | 140 | pub fn root_dir(&self) -> &Path { |
134 | &sysroot.crates[self].name | 141 | self.root.parent().unwrap() |
135 | } | ||
136 | pub fn root(self, sysroot: &Sysroot) -> &Path { | ||
137 | sysroot.crates[self].root.as_path() | ||
138 | } | ||
139 | pub fn root_dir(self, sysroot: &Sysroot) -> &Path { | ||
140 | self.root(sysroot).parent().unwrap() | ||
141 | } | ||
142 | pub fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a { | ||
143 | sysroot.crates[self].deps.iter().copied() | ||
144 | } | 142 | } |
145 | } | 143 | } |
146 | 144 | ||