1use std::mem;
2
3use doc::{Data, Document, Identifier, Object};
4use error::Error;
5use query::Query;
6use value::Set;
7use value::fields::Key;
8use view::{Context, Render};
9
10pub trait Resource {
33 fn kind() -> Key;
56
57 fn id(&self) -> String;
80
81 fn to_ident(&self, ctx: &mut Context) -> Result<Identifier, Error>;
89
90 fn to_object(&self, ctx: &mut Context) -> Result<Object, Error>;
97}
98
99impl<'a, T: Resource> Render<Identifier> for &'a T {
100 fn render(self, query: Option<&Query>) -> Result<Document<Identifier>, Error> {
101 let mut incl = Set::new();
102 let mut ctx = Context::new(T::kind(), query, &mut incl);
103
104 self.to_ident(&mut ctx)?.render(query)
105 }
106}
107
108impl<'a, T: Resource> Render<Identifier> for &'a [T] {
109 fn render(self, query: Option<&Query>) -> Result<Document<Identifier>, Error> {
110 let mut incl = Set::new();
111 let mut ctx = Context::new(T::kind(), query, &mut incl);
112
113 self.into_iter()
114 .map(|item| item.to_ident(&mut ctx))
115 .collect::<Result<Vec<_>, _>>()?
116 .render(query)
117 }
118}
119
120impl<'a, T: Resource> Render<Object> for &'a T {
121 fn render(self, query: Option<&Query>) -> Result<Document<Object>, Error> {
122 let mut incl = Set::new();
123 let (data, links, meta) = {
124 let mut ctx = Context::new(T::kind(), query, &mut incl);
125 let mut obj = self.to_object(&mut ctx)?;
126 let links = mem::replace(&mut obj.links, Default::default());
127 let meta = mem::replace(&mut obj.meta, Default::default());
128
129 (obj.into(), links, meta)
130 };
131
132 Ok(Document::Ok {
133 data,
134 links,
135 meta,
136 included: incl,
137 jsonapi: Default::default(),
138 })
139 }
140}
141
142impl<'a, T: Resource> Render<Object> for &'a [T] {
143 fn render(self, query: Option<&Query>) -> Result<Document<Object>, Error> {
144 let mut incl = Set::new();
145 let mut data = Vec::with_capacity(self.len());
146
147 {
148 let mut ctx = Context::new(T::kind(), query, &mut incl);
149
150 for item in self {
151 data.push(item.to_object(&mut ctx)?);
152 }
153 }
154
155 Ok(Document::Ok {
156 data: Data::Collection(data),
157 links: Default::default(),
158 meta: Default::default(),
159 included: incl,
160 jsonapi: Default::default(),
161 })
162 }
163}
164
165#[macro_export]
310macro_rules! resource {
311 ($target:ident, |&$this:ident| { $($rest:tt)* }) => {
312 impl $crate::Resource for $target {
313 fn kind() -> $crate::value::Key {
314 let raw = extract_resource_kind!({ $($rest)* }).to_owned();
315 $crate::value::Key::from_raw(raw)
316 }
317
318 fn id(&$this) -> String {
319 use $crate::value::Stringify as JsonApiStringifyTrait;
320 extract_resource_id!({ $($rest)* }).stringify()
321 }
322
323 fn to_ident(
324 &$this,
325 _: &mut $crate::view::Context,
326 ) -> Result<$crate::doc::Identifier, $crate::Error> {
327 let mut ident = {
328 let kind = <$target as $crate::Resource>::kind();
329 let id = $crate::Resource::id($this);
330
331 $crate::doc::Identifier::new(kind, id)
332 };
333
334 {
335 let _meta = &mut ident.meta;
336 expand_resource_impl!(@meta $this, _meta, {
337 $($rest)*
338 });
339 }
340
341 Ok(ident)
342 }
343
344 fn to_object(
345 &$this,
346 ctx: &mut $crate::view::Context,
347 ) -> Result<$crate::doc::Object, $crate::error::Error> {
348 #[allow(dead_code)]
349 fn item_kind<T: $crate::Resource>(_: &T) -> $crate::value::Key {
350 T::kind()
351 }
352
353 #[allow(dead_code)]
354 fn iter_kind<'a, I, T>(_: &I) -> $crate::value::Key
355 where
356 I: Iterator<Item = &'a T>,
357 T: $crate::Resource + 'a,
358 {
359 T::kind()
360 }
361
362 let mut obj = {
363 let kind = <$target as $crate::Resource>::kind();
364 let id = $crate::Resource::id($this);
365
366 $crate::doc::Object::new(kind, id)
367 };
368
369 {
370 let _attrs = &mut obj.attributes;
371 expand_resource_impl!(@attrs $this, _attrs, ctx, {
372 $($rest)*
373 });
374 }
375
376 {
377 let _links = &mut obj.links;
378 expand_resource_impl!(@links $this, _links, {
379 $($rest)*
380 });
381 }
382
383 {
384 let _meta = &mut obj.meta;
385 expand_resource_impl!(@meta $this, _meta, {
386 $($rest)*
387 });
388 }
389
390 {
391 let _related = &mut obj.relationships;
392 expand_resource_impl!(@rel $this, _related, ctx, {
393 $($rest)*
394 });
395 }
396
397 Ok(obj)
398 }
399 }
400 };
401}
402
403#[doc(hidden)]
404#[macro_export]
405macro_rules! expand_resource_impl {
406 (@attrs $this:ident, $attrs:ident, $ctx:ident, {
407 attr $key:expr, $value:block
408 $($rest:tt)*
409 }) => {
410 if $ctx.field($key) {
411 let key = $key.parse::<$crate::value::Key>()?;
412 let value = $crate::to_value($value)?;
413
414 $attrs.insert(key, value);
415 }
416
417 expand_resource_impl!(@attrs $this, $attrs, $ctx, {
418 $($rest)*
419 });
420 };
421
422 (@attrs $this:ident, $($arg:ident),*, { attr $field:ident; $($rest:tt)* }) => {
423 expand_resource_impl!(@attrs $this, $($arg),*, {
424 attr stringify!($field), &$this.$field;
425 $($rest)*
426 });
427 };
428
429 (@attrs $($arg:ident),*, { attrs $($field:ident),+; $($rest:tt)* }) => {
430 expand_resource_impl!(@attrs $($arg),*, {
431 $(attr $field;)+
432 $($rest)*
433 });
434 };
435
436 (@rel $this:ident, $related:ident, $ctx:ident, {
437 has_many $key:expr, { $($body:tt)* }
438 $($rest:tt)*
439 }) => {
440 if $ctx.field($key) {
441 let key = $key.parse::<$crate::value::Key>()?;
442 expand_resource_impl!(@has_many $this, $related, key, $ctx, {
443 $($body)*
444 });
445 }
446
447 expand_resource_impl!(@rel $this, $related, $ctx, {
448 $($rest)*
449 });
450 };
451
452 (@rel $this:ident, $related:ident, $ctx:ident, {
453 has_one $key:expr, { $($body:tt)* }
454 $($rest:tt)*
455 }) => {
456 if $ctx.field($key) {
457 let key = $key.parse::<$crate::value::Key>()?;
458 expand_resource_impl!(@has_one $this, $related, key, $ctx, {
459 $($body)*
460 });
461 }
462
463 expand_resource_impl!(@rel $this, $related, $ctx, {
464 $($rest)*
465 });
466 };
467
468 (@rel $this:ident, $($arg:ident),*, {
469 has_many $($field:ident),*;
470 $($rest:tt)*
471 }) => {
472 expand_resource_impl!(@rel $this, $($arg),*, {
473 $(has_many stringify!($field), { data $this.$field.iter(); })*
474 $($rest)*
475 });
476 };
477
478 (@rel $this:ident, $($arg:ident),*, {
479 has_one $($field:ident),*;
480 $($rest:tt)*
481 }) => {
482 expand_resource_impl!(@rel $this, $($arg),*, {
483 $(has_one stringify!($field), { data $this.$field.as_ref(); })*
484 $($rest)*
485 });
486 };
487
488 (@has_many $this:ident, $related:ident, $key:ident, $ctx:ident, {
489 data $value:block
490 $($rest:tt)*
491 }) => {
492 let mut rel = $crate::doc::Relationship::new({
493 let mut ctx = $ctx.fork(iter_kind(&$value), &$key);
494 let mut data = match $value.size_hint() {
495 (_, Some(size)) => Vec::with_capacity(size),
496 _ => Vec::new(),
497 };
498
499 if ctx.included() {
500 for item in $value {
501 let object = $crate::Resource::to_object(item, &mut ctx)?;
502 let ident = $crate::doc::Identifier::from(&object);
503
504 ctx.include(object);
505 data.push(ident);
506 }
507 } else {
508 for item in $value {
509 data.push($crate::Resource::to_ident(item, &mut ctx)?);
510 }
511 }
512
513 data.into()
514 });
515
516 {
517 let links = &mut rel.links;
518 expand_resource_impl!(@links $this, links, {
519 $($rest)*
520 });
521 }
522
523 {
524 let _meta = &mut rel.meta;
525 expand_resource_impl!(@meta $this, _meta, {
526 $($rest)*
527 });
528 }
529
530 $related.insert($key, rel);
531 };
532
533 (@has_one $this:ident, $related:ident, $key:ident, $ctx:ident, {
534 data $value:block
535 $($rest:tt)*
536 }) => {
537 let mut rel = $crate::doc::Relationship::new({
538 let mut data = None;
539
540 if let Some(item) = $value {
541 let mut ctx = $ctx.fork(item_kind(item), &$key);
542
543 data = Some($crate::Resource::to_ident(item, &mut ctx)?);
544
545 if ctx.included() {
546 let object = $crate::Resource::to_object(item, &mut ctx)?;
547 ctx.include(object);
548 }
549 }
550
551 data.into()
552 });
553
554 {
555 let _links = &mut rel.links;
556 expand_resource_impl!(@links $this, _links, {
557 $($rest)*
558 });
559 }
560
561 {
562 let _meta = &mut rel.meta;
563 expand_resource_impl!(@meta $this, _meta, {
564 $($rest)*
565 });
566 }
567
568 $related.insert($key, rel);
569 };
570
571 (@links $this:ident, $links:ident, {
572 link $key:expr, { $($body:tt)* }
573 $($rest:tt)*
574 }) => {
575 {
576 let key = $key.parse::<$crate::value::Key>()?;
577 let link = expand_resource_impl!(@link $this, {
578 $($body)*
579 });
580
581 $links.insert(key, link);
582 }
583
584 expand_resource_impl!(@links $this, $links, {
585 $($rest)*
586 });
587 };
588
589 (@links $($args:ident),+, {
590 link $key:expr, $value:expr;
591 $($rest:tt)*
592 }) => {
593 expand_resource_impl!(@links $($args),+, {
594 link $key, { href { $value } }
595 $($rest)*
596 });
597 };
598
599 (@link $this:ident, { href $value:block $($rest:tt)* }) => {{
600 let mut link = $value.parse::<$crate::doc::Link>()?;
601
602 {
603 let _meta = &link.meta;
604 expand_resource_impl!(@meta $this, _meta, {
605 $($rest)*
606 });
607 }
608
609 link
610 }};
611
612 (@meta $this:ident, $meta:ident, {
613 meta $key:expr, $value:block
614 $($rest:tt)*
615 }) => {
616 {
617 let key = $key.parse::<$crate::value::Key>()?;
618 let value = $crate::to_value($value)?;
619
620 $meta.insert(key, value);
621 }
622
623 expand_resource_impl!(@meta $this, $meta, {
624 $($rest)*
625 });
626 };
627
628 (@$scope:tt $($args:ident),+, {
630 has_many $key:expr, { $($body:tt)* }
631 $($rest:tt)*
632 }) => {
633 expand_resource_impl!(@$scope $($args),+, {
634 $($rest)*
635 });
636 };
637
638 (@$scope:tt $($args:ident),+, {
640 has_one $key:expr, { $($body:tt)* }
641 $($rest:tt)*
642 }) => {
643 expand_resource_impl!(@$scope $($args),+, {
644 $($rest)*
645 });
646 };
647
648 (@$scope:tt $($args:ident),+, {
650 link $key:expr, { $($body:tt)* }
651 $($rest:tt)*
652 }) => {
653 expand_resource_impl!(@$scope $($args),+, {
654 $($rest)*
655 });
656 };
657
658 (@$scope:tt $($args:ident),+, {
659 $kwd:ident $value:expr;
660 $($rest:tt)*
661 }) => {
662 expand_resource_impl!(@$scope $($args),+, {
663 $kwd { $value }
664 $($rest)*
665 });
666 };
667
668 (@$scope:tt $($args:ident),+, {
669 has_many $key:expr, $value:block
670 $($rest:tt)*
671 }) => {
672 expand_resource_impl!(@$scope $($args),+, {
673 $($rest)*
674 });
675 };
676
677 (@$scope:tt $($args:ident),+, {
678 has_one $key:expr, $value:block
679 $($rest:tt)*
680 }) => {
681 expand_resource_impl!(@$scope $($args),+, {
682 $($rest)*
683 });
684 };
685
686 (@$scope:tt $($args:ident),+, {
687 link $key:expr, $value:block
688 $($rest:tt)*
689 }) => {
690 expand_resource_impl!(@$scope $($args),+, {
691 $($rest)*
692 });
693 };
694
695 (@$scope:tt $($args:ident),+, {
696 $kwd:ident $key:expr, $value:expr;
697 $($rest:tt)*
698 }) => {
699 expand_resource_impl!(@$scope $($args),+, {
700 $kwd $key, { $value }
701 $($rest)*
702 });
703 };
704
705 (@$scope:tt $($args:ident),+, {
706 $skip:tt
707 $($rest:tt)*
708 }) => {
709 expand_resource_impl!(@$scope $($args),+, {
710 $($rest)*
711 });
712 };
713
714 ($($rest:tt)*) => ();
715}
716
717#[doc(hidden)]
718#[macro_export]
719macro_rules! extract_resource_id {
720 ({ id $value:block $($rest:tt)* }) => { $value };
721 ({ id $value:expr; $($rest:tt)* }) => { $value };
722 ({ $skip:tt $($rest:tt)* }) => { extract_resource_id!({ $($rest)* }) };
723 ({ $($rest:tt)* }) => ();
724}
725
726#[doc(hidden)]
727#[macro_export]
728macro_rules! extract_resource_kind {
729 ({ kind $value:block $($rest:tt)* }) => { $value };
730 ({ kind $value:expr; $($rest:tt)* }) => { $value };
731 ({ $skip:tt $($rest:tt)* }) => { extract_resource_kind!({ $($rest)* }) };
732 ({ $($rest:tt)* }) => ();
733}