1use byteorder::{LittleEndian, WriteBytesExt};
6use fdomain_client::AsHandleRef;
7use fdomain_client::fidl::{RequestStream as FRequestStream, ServerEnd as FServerEnd};
8use fdomain_fuchsia_io as fio_f;
9use fdomain_fuchsia_sys2 as fsys2_f;
10use fidl::endpoints::{ServerEnd, create_endpoints, create_proxy};
11use fidl_fuchsia_component_decl::{
12 Capability, Component, Dictionary, Expose, ExposeDictionary, ExposeProtocol, ParentRef,
13 Protocol, Ref, SelfRef,
14};
15use fidl_fuchsia_io as fio;
16use fidl_fuchsia_sys2 as fsys2;
17use futures::{StreamExt, TryStreamExt};
18use moniker::Moniker;
19use std::collections::HashMap;
20use std::io::Write;
21use std::path::Path;
22use std::rc::Rc;
23use std::str::FromStr;
24use std::sync::atomic::{AtomicBool, Ordering};
25use zx_status::Status;
26
27#[derive(Default)]
46pub struct MockRealmQueryBuilder {
47 mapping: HashMap<String, Box<MockRealmQueryBuilderInner>>,
48}
49
50pub struct MockRealmQueryBuilderInner {
53 when: Moniker,
54 moniker: Moniker,
55 exposes: Vec<Expose>,
56 parent: Option<Box<MockRealmQueryBuilder>>,
57}
58
59impl MockRealmQueryBuilderInner {
60 pub fn moniker(mut self, moniker: &str) -> Self {
62 self.moniker = moniker.try_into().unwrap();
63 self
64 }
65
66 pub fn exposes(mut self, exposes: Vec<Expose>) -> Self {
68 self.exposes = exposes;
69 self
70 }
71
72 pub fn accessors(mut self, accessors: &[&str]) -> Self {
74 let mut exposes = vec![Expose::Dictionary(ExposeDictionary {
75 source_name: Some("diagnostics-accessors".into()),
76 target_name: Some("diagnostics-accessors".into()),
77 source: Some(Ref::Self_(SelfRef)),
78 target: Some(Ref::Parent(ParentRef)),
79 ..Default::default()
80 })];
81 for accessor in accessors {
82 exposes.push(Expose::Protocol(ExposeProtocol {
83 source: Some(Ref::Self_(SelfRef)),
84 target: Some(Ref::Parent(ParentRef)),
85 source_name: Some((*accessor).to_owned()),
86 target_name: Some((*accessor).to_owned()),
87 source_dictionary: Some("diagnostics-accessors".to_owned()),
88 ..Default::default()
89 }));
90 }
91 self.exposes = exposes;
92 self
93 }
94
95 pub fn add(mut self) -> MockRealmQueryBuilder {
97 let mut parent = *self.parent.unwrap();
98 self.parent = None;
99
100 parent.mapping.insert(self.when.to_string(), Box::new(self));
101 parent
102 }
103
104 pub fn serve_exposed_dir_f(&self, server_end: FServerEnd<fio_f::DirectoryMarker>, path: &str) {
105 let mut mock_dir_top = MockDir::new("expose".to_owned());
106 let mut mock_accessors = MockDir::new("diagnostics-accessors".to_owned());
107 for expose in &self.exposes {
108 let Expose::Protocol(ExposeProtocol {
109 source_name: Some(name), source_dictionary, ..
110 }) = expose
111 else {
112 continue;
113 };
114 if matches!(source_dictionary, Some(d) if d == "diagnostics-accessors") {
115 mock_accessors = mock_accessors.add_entry(MockFile::new_arc(name.to_owned()));
116 }
117 }
118
119 match path {
120 "diagnostics-accessors" => {
121 fuchsia_async::Task::local(async move {
122 Rc::new(mock_accessors).serve_f(server_end).await
123 })
124 .detach();
125 }
126 _ => {
127 mock_dir_top = mock_dir_top.add_entry(Rc::new(mock_accessors));
128 fuchsia_async::Task::local(async move {
129 Rc::new(mock_dir_top).serve_f(server_end).await
130 })
131 .detach();
132 }
133 }
134 }
135
136 pub fn serve_exposed_dir(&self, server_end: ServerEnd<fio::DirectoryMarker>, path: &str) {
137 let mut mock_dir_top = MockDir::new("expose".to_owned());
138 let mut mock_accessors = MockDir::new("diagnostics-accessors".to_owned());
139 for expose in &self.exposes {
140 let Expose::Protocol(ExposeProtocol {
141 source_name: Some(name), source_dictionary, ..
142 }) = expose
143 else {
144 continue;
145 };
146 if matches!(source_dictionary, Some(d) if d == "diagnostics-accessors") {
147 mock_accessors = mock_accessors.add_entry(MockFile::new_arc(name.to_owned()));
148 }
149 }
150
151 match path {
152 "diagnostics-accessors" => {
153 fuchsia_async::Task::local(async move {
154 Rc::new(mock_accessors).serve(server_end).await
155 })
156 .detach();
157 }
158 _ => {
159 mock_dir_top = mock_dir_top.add_entry(Rc::new(mock_accessors));
160 fuchsia_async::Task::local(
161 async move { Rc::new(mock_dir_top).serve(server_end).await },
162 )
163 .detach();
164 }
165 }
166 }
167
168 fn to_instance(&self) -> fsys2::Instance {
169 fsys2::Instance {
170 moniker: Some(self.moniker.to_string()),
171 url: Some("".to_owned()),
172 instance_id: None,
173 resolved_info: Some(fsys2::ResolvedInfo {
174 resolved_url: Some("".to_owned()),
175 ..Default::default()
176 }),
177 ..Default::default()
178 }
179 }
180
181 fn make_manifest(&self) -> Component {
182 let capabilities = self
183 .exposes
184 .iter()
185 .map(|expose| match expose {
186 Expose::Protocol(ExposeProtocol {
187 source_name: Some(name),
188 source: Some(Ref::Self_(SelfRef)),
189 ..
190 }) => Capability::Protocol(Protocol {
191 name: Some(name.clone()),
192 source_path: Some(format!("/svc/{name}")),
193 ..Protocol::default()
194 }),
195 Expose::Dictionary(ExposeDictionary {
196 source_name: Some(name),
197 source: Some(Ref::Self_(SelfRef)),
198 ..
199 }) => Capability::Dictionary(Dictionary {
200 name: Some(name.clone()),
201 source: Some(Ref::Self_(SelfRef)),
202 ..Dictionary::default()
203 }),
204 _ => unreachable!("we just add protocols for the test purposes"),
205 })
206 .collect::<Vec<_>>();
207 Component {
208 capabilities: Some(capabilities),
209 exposes: Some(self.exposes.clone()),
210 ..Default::default()
211 }
212 }
213}
214
215impl MockRealmQueryBuilder {
216 pub fn new() -> Self {
218 Self::default()
219 }
220
221 pub fn when(self, at: &str) -> MockRealmQueryBuilderInner {
224 MockRealmQueryBuilderInner {
225 when: at.try_into().unwrap(),
226 moniker: Moniker::root(),
227 exposes: vec![],
228 parent: Some(Box::new(self)),
229 }
230 }
231
232 pub fn build(self) -> MockRealmQuery {
234 MockRealmQuery { mapping: self.mapping }
235 }
236
237 pub fn prefilled() -> Self {
238 Self::new()
239 .when("example/component")
240 .moniker("./example/component")
241 .exposes(vec![
242 Expose::Protocol(ExposeProtocol {
243 source: Some(Ref::Self_(SelfRef)),
244 target: Some(Ref::Parent(ParentRef)),
245 source_name: Some("fuchsia.diagnostics.ArchiveAccessor".to_owned()),
246 target_name: Some("fuchsia.diagnostics.ArchiveAccessor".to_owned()),
247 source_dictionary: Some("diagnostics-accessors".to_owned()),
248 ..Default::default()
249 }),
250 Expose::Dictionary(ExposeDictionary {
251 source_name: Some("diagnostics-accessors".into()),
252 target_name: Some("diagnostics-accessors".into()),
253 source: Some(Ref::Self_(SelfRef)),
254 target: Some(Ref::Parent(ParentRef)),
255 ..Default::default()
256 }),
257 ])
258 .add()
259 .when("other/component")
260 .moniker("./other/component")
261 .exposes(vec![Expose::Protocol(ExposeProtocol {
262 source: Some(Ref::Self_(SelfRef)),
263 target: Some(Ref::Parent(ParentRef)),
264 source_name: Some("src".to_owned()),
265 target_name: Some("fuchsia.io.SomeOtherThing".to_owned()),
266 ..Default::default()
267 })])
268 .add()
269 .when("other/component")
270 .moniker("./other/component")
271 .exposes(vec![Expose::Protocol(ExposeProtocol {
272 source: Some(Ref::Self_(SelfRef)),
273 target: Some(Ref::Parent(ParentRef)),
274 source_name: Some("src".to_owned()),
275 target_name: Some("fuchsia.io.MagicStuff".to_owned()),
276 ..Default::default()
277 })])
278 .add()
279 .when("foo/component")
280 .moniker("./foo/component")
281 .exposes(vec![
282 Expose::Protocol(ExposeProtocol {
283 source: Some(Ref::Self_(SelfRef)),
284 target: Some(Ref::Parent(ParentRef)),
285 source_name: Some("fuchsia.diagnostics.ArchiveAccessor.feedback".to_owned()),
286 target_name: Some("fuchsia.diagnostics.ArchiveAccessor.feedback".to_owned()),
287 source_dictionary: Some("diagnostics-accessors".to_owned()),
288 ..Default::default()
289 }),
290 Expose::Dictionary(ExposeDictionary {
291 source_name: Some("diagnostics-accessors".into()),
292 target_name: Some("diagnostics-accessors".into()),
293 source: Some(Ref::Self_(SelfRef)),
294 target: Some(Ref::Parent(ParentRef)),
295 ..Default::default()
296 }),
297 ])
298 .add()
299 .when("foo/bar/thing:instance")
300 .moniker("./foo/bar/thing:instance")
301 .exposes(vec![
302 Expose::Protocol(ExposeProtocol {
303 source: Some(Ref::Self_(SelfRef)),
304 target: Some(Ref::Parent(ParentRef)),
305 source_name: Some("fuchsia.diagnostics.ArchiveAccessor.feedback".to_owned()),
306 target_name: Some("fuchsia.diagnostics.ArchiveAccessor.feedback".to_owned()),
307 source_dictionary: Some("diagnostics-accessors".to_owned()),
308 ..Default::default()
309 }),
310 Expose::Dictionary(ExposeDictionary {
311 source_name: Some("diagnostics-accessors".into()),
312 target_name: Some("diagnostics-accessors".into()),
313 source: Some(Ref::Self_(SelfRef)),
314 target: Some(Ref::Parent(ParentRef)),
315 ..Default::default()
316 }),
317 ])
318 .add()
319 }
320}
321
322pub struct MockRealmQuery {
324 mapping: HashMap<String, Box<MockRealmQueryBuilderInner>>,
326}
327
328impl Default for MockRealmQuery {
330 fn default() -> Self {
331 MockRealmQueryBuilder::prefilled().build()
332 }
333}
334
335impl MockRealmQuery {
336 pub async fn serve_f(self: Rc<Self>, object: FServerEnd<fsys2_f::RealmQueryMarker>) {
338 let client = object.domain();
339 let mut stream = object.into_stream();
340 while let Ok(Some(request)) = stream.try_next().await {
341 match request {
342 fsys2_f::RealmQueryRequest::GetInstance { moniker, responder } => {
343 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
344 let res = self.mapping.get(&query_moniker.to_string()).unwrap();
345 responder.send(Ok(&res.to_instance())).unwrap();
346 }
347 fsys2_f::RealmQueryRequest::OpenDirectory {
348 moniker,
349 dir_type,
350 object,
351 responder,
352 ..
353 } => {
354 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
355 if let Some(res) = self.mapping.get(&query_moniker.to_string()) {
356 if dir_type == fsys2_f::OpenDirType::ExposedDir {
357 res.serve_exposed_dir_f(object, "");
359 }
360 responder.send(Ok(())).unwrap();
361 } else {
362 responder.send(Err(fsys2_f::OpenError::InstanceNotFound)).unwrap();
363 }
364 }
365 fsys2_f::RealmQueryRequest::GetResolvedDeclaration {
366 moniker, responder, ..
367 } => {
368 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
369 let res = self.mapping.get(&query_moniker.to_string()).unwrap();
370 let manifest = res.make_manifest();
371 let manifest = fidl::persist(&manifest).unwrap();
372 let (client_end, server_end) =
373 client.create_endpoints::<fsys2_f::ManifestBytesIteratorMarker>();
374
375 fuchsia_async::Task::spawn(async move {
376 let mut stream = server_end.into_stream();
377 let fsys2_f::ManifestBytesIteratorRequest::Next { responder } =
378 stream.next().await.unwrap().unwrap();
379 responder.send(manifest.as_slice()).unwrap();
380 let fsys2_f::ManifestBytesIteratorRequest::Next { responder } =
381 stream.next().await.unwrap().unwrap();
382 responder.send(&[]).unwrap();
383 })
384 .detach();
385
386 responder.send(Ok(client_end)).unwrap();
387 }
388 fsys2_f::RealmQueryRequest::GetAllInstances { responder } => {
389 let instances: Vec<fsys2_f::Instance> =
390 self.mapping.values().map(|m| m.to_instance()).collect();
391
392 let (client_end, server_end) =
393 client.create_endpoints::<fsys2_f::InstanceIteratorMarker>();
394
395 fuchsia_async::Task::spawn(async move {
396 let mut stream = server_end.into_stream();
397 let fsys2_f::InstanceIteratorRequest::Next { responder } =
398 stream.next().await.unwrap().unwrap();
399 responder.send(&instances).unwrap();
400 let fsys2_f::InstanceIteratorRequest::Next { responder } =
401 stream.next().await.unwrap().unwrap();
402 responder.send(&[]).unwrap();
403 })
404 .detach();
405
406 responder.send(Ok(client_end)).unwrap();
407 }
408 _ => unreachable!("request {:?}", request),
409 }
410 }
411 }
412
413 pub async fn serve(self: Rc<Self>, object: ServerEnd<fsys2::RealmQueryMarker>) {
415 let mut stream = object.into_stream();
416 while let Ok(Some(request)) = stream.try_next().await {
417 match request {
418 fsys2::RealmQueryRequest::GetInstance { moniker, responder } => {
419 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
420 let res = self.mapping.get(&query_moniker.to_string()).unwrap();
421 responder.send(Ok(&res.to_instance())).unwrap();
422 }
423 fsys2::RealmQueryRequest::OpenDirectory {
424 moniker,
425 dir_type,
426 object,
427 responder,
428 ..
429 } => {
430 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
431 if let Some(res) = self.mapping.get(&query_moniker.to_string()) {
432 if dir_type == fsys2::OpenDirType::ExposedDir {
433 res.serve_exposed_dir(object, "");
435 }
436 responder.send(Ok(())).unwrap();
437 } else {
438 responder.send(Err(fsys2::OpenError::InstanceNotFound)).unwrap();
439 }
440 }
441 fsys2::RealmQueryRequest::GetResolvedDeclaration { moniker, responder, .. } => {
442 let query_moniker = Moniker::from_str(moniker.as_str()).unwrap();
443 let res = self.mapping.get(&query_moniker.to_string()).unwrap();
444 let manifest = res.make_manifest();
445 let manifest = fidl::persist(&manifest).unwrap();
446 let (client_end, server_end) =
447 create_endpoints::<fsys2::ManifestBytesIteratorMarker>();
448
449 fuchsia_async::Task::spawn(async move {
450 let mut stream = server_end.into_stream();
451 let fsys2::ManifestBytesIteratorRequest::Next { responder } =
452 stream.next().await.unwrap().unwrap();
453 responder.send(manifest.as_slice()).unwrap();
454 let fsys2::ManifestBytesIteratorRequest::Next { responder } =
455 stream.next().await.unwrap().unwrap();
456 responder.send(&[]).unwrap();
457 })
458 .detach();
459
460 responder.send(Ok(client_end)).unwrap();
461 }
462 fsys2::RealmQueryRequest::GetAllInstances { responder } => {
463 let instances: Vec<fsys2::Instance> =
464 self.mapping.values().map(|m| m.to_instance()).collect();
465
466 let (client_end, server_end) =
467 create_endpoints::<fsys2::InstanceIteratorMarker>();
468
469 fuchsia_async::Task::spawn(async move {
470 let mut stream = server_end.into_stream();
471 let fsys2::InstanceIteratorRequest::Next { responder } =
472 stream.next().await.unwrap().unwrap();
473 responder.send(&instances).unwrap();
474 let fsys2::InstanceIteratorRequest::Next { responder } =
475 stream.next().await.unwrap().unwrap();
476 responder.send(&[]).unwrap();
477 })
478 .detach();
479
480 responder.send(Ok(client_end)).unwrap();
481 }
482 _ => unreachable!("request {:?}", request),
483 }
484 }
485 }
486
487 pub async fn get_proxy(self: Rc<Self>) -> fsys2::RealmQueryProxy {
491 let (proxy, server_end) = create_proxy::<fsys2::RealmQueryMarker>();
492 fuchsia_async::Task::local(async move { self.serve(server_end).await }).detach();
493 proxy
494 }
495}
496
497pub trait Entry {
499 fn open(self: Rc<Self>, path: &str, object: fidl::Channel);
500 fn open_f(self: Rc<Self>, path: &str, object: fdomain_client::Channel);
501 fn encode(&self, buf: &mut Vec<u8>);
502 fn name(&self) -> String;
503}
504
505pub struct MockDir {
506 subdirs: HashMap<String, Rc<dyn Entry>>,
507 name: String,
508 at_end: AtomicBool,
509}
510
511impl MockDir {
512 pub fn new(name: String) -> Self {
513 MockDir { name, subdirs: HashMap::new(), at_end: AtomicBool::new(false) }
514 }
515
516 pub fn new_arc(name: String) -> Rc<Self> {
517 Rc::new(Self::new(name))
518 }
519
520 pub fn add_entry(mut self, entry: Rc<dyn Entry>) -> Self {
521 self.subdirs.insert(entry.name(), entry);
522 self
523 }
524
525 async fn serve_f(self: Rc<Self>, object: FServerEnd<fio_f::DirectoryMarker>) {
526 let mut stream = object.into_stream();
527 let _ = stream.control_handle().send_on_open_(
528 zx_status::sys::ZX_OK,
529 Some(fio_f::NodeInfoDeprecated::Directory(fio_f::DirectoryObject {})),
530 );
531 while let Ok(Some(request)) = stream.try_next().await {
532 match request {
533 fio_f::DirectoryRequest::Open { path, object, .. } => {
534 self.clone().open_f(&path, object);
535 }
536 fio_f::DirectoryRequest::Rewind { responder, .. } => {
537 self.at_end.store(false, Ordering::Relaxed);
538 responder.send(zx_status::sys::ZX_OK).unwrap();
539 }
540 fio_f::DirectoryRequest::ReadDirents { max_bytes: _, responder, .. } => {
541 let entries = match self.at_end.compare_exchange(
542 false,
543 true,
544 Ordering::Relaxed,
545 Ordering::Relaxed,
546 ) {
547 Ok(false) => encode_entries(&self.subdirs),
548 Err(true) => Vec::new(),
549 _ => unreachable!(),
550 };
551 responder.send(zx_status::sys::ZX_OK, &entries).unwrap();
552 }
553 x => panic!("unsupported request: {x:?}"),
554 }
555 }
556 }
557
558 async fn serve(self: Rc<Self>, object: ServerEnd<fio::DirectoryMarker>) {
559 let mut stream = object.into_stream();
560 while let Ok(Some(request)) = stream.try_next().await {
561 match request {
562 fio::DirectoryRequest::Open { path, object, .. } => {
563 self.clone().open(&path, object);
564 }
565 fio::DirectoryRequest::Rewind { responder, .. } => {
566 self.at_end.store(false, Ordering::Relaxed);
567 responder.send(zx_status::sys::ZX_OK).unwrap();
568 }
569 fio::DirectoryRequest::ReadDirents { max_bytes: _, responder, .. } => {
570 let entries = match self.at_end.compare_exchange(
571 false,
572 true,
573 Ordering::Relaxed,
574 Ordering::Relaxed,
575 ) {
576 Ok(false) => encode_entries(&self.subdirs),
577 Err(true) => Vec::new(),
578 _ => unreachable!(),
579 };
580 responder.send(zx_status::sys::ZX_OK, &entries).unwrap();
581 }
582 x => panic!("unsupported request: {x:?}"),
583 }
584 }
585 }
586}
587
588fn encode_entries(subdirs: &HashMap<String, Rc<dyn Entry>>) -> Vec<u8> {
589 let mut buf = Vec::new();
590 let mut data = subdirs.iter().collect::<Vec<(_, _)>>();
591 data.sort_by(|a, b| a.0.cmp(b.0));
592 for (_, entry) in data.iter() {
593 entry.encode(&mut buf);
594 }
595 buf
596}
597
598impl Entry for MockDir {
599 fn open(self: Rc<Self>, path: &str, object: fidl::Channel) {
600 let path = Path::new(path);
601 let mut path_iter = path.iter();
602 let segment = if let Some(segment) = path_iter.next() {
603 if let Some(segment) = segment.to_str() {
604 segment
605 } else {
606 let _ =
607 ServerEnd::<fio::NodeMarker>::new(object).close_with_epitaph(Status::NOT_FOUND);
608 return;
609 }
610 } else {
611 "."
612 };
613 if segment == "." {
614 fuchsia_async::Task::local(self.serve(ServerEnd::new(object))).detach();
615 return;
616 }
617 if let Some(entry) = self.subdirs.get(segment) {
618 entry.clone().open(path_iter.as_path().to_str().unwrap(), object);
619 } else {
620 let _ = ServerEnd::<fio::NodeMarker>::new(object).close_with_epitaph(Status::NOT_FOUND);
621 }
622 }
623
624 fn open_f(self: Rc<Self>, path: &str, object: fdomain_client::Channel) {
625 let path = Path::new(path);
626 let mut path_iter = path.iter();
627 let segment = if let Some(segment) = path_iter.next() {
628 if let Some(segment) = segment.to_str() {
629 segment
630 } else {
631 let _ = FServerEnd::<fio_f::NodeMarker>::new(object)
632 .close_with_epitaph(Status::NOT_FOUND);
633 return;
634 }
635 } else {
636 "."
637 };
638 if segment == "." {
639 fuchsia_async::Task::local(self.serve_f(FServerEnd::new(object))).detach();
640 return;
641 }
642 if let Some(entry) = self.subdirs.get(segment) {
643 entry.clone().open_f(path_iter.as_path().to_str().unwrap(), object);
644 } else {
645 let _ =
646 FServerEnd::<fio_f::NodeMarker>::new(object).close_with_epitaph(Status::NOT_FOUND);
647 }
648 }
649
650 fn encode(&self, buf: &mut Vec<u8>) {
651 buf.write_u64::<LittleEndian>(fio::INO_UNKNOWN).expect("writing mockdir ino to work");
652 buf.write_u8(self.name.len() as u8).expect("writing mockdir size to work");
653 buf.write_u8(fio::DirentType::Directory.into_primitive())
654 .expect("writing mockdir type to work");
655 buf.write_all(self.name.as_ref()).expect("writing mockdir name to work");
656 }
657
658 fn name(&self) -> String {
659 self.name.clone()
660 }
661}
662
663struct MockFile {
664 name: String,
665}
666
667impl MockFile {
668 pub fn new(name: String) -> Self {
669 MockFile { name }
670 }
671 pub fn new_arc(name: String) -> Rc<Self> {
672 Rc::new(Self::new(name))
673 }
674}
675
676impl Entry for MockFile {
677 fn open(self: Rc<Self>, _path: &str, _object: fidl::Channel) {
678 unimplemented!();
679 }
680
681 fn open_f(self: Rc<Self>, _path: &str, _object: fdomain_client::Channel) {
682 unimplemented!();
683 }
684
685 fn encode(&self, buf: &mut Vec<u8>) {
686 buf.write_u64::<LittleEndian>(fio::INO_UNKNOWN).expect("writing mockdir ino to work");
687 buf.write_u8(self.name.len() as u8).expect("writing mockdir size to work");
688 buf.write_u8(fio::DirentType::Service.into_primitive())
689 .expect("writing mockdir type to work");
690 buf.write_all(self.name.as_ref()).expect("writing mockdir name to work");
691 }
692
693 fn name(&self) -> String {
694 self.name.clone()
695 }
696}