@@ -353,21 +353,38 @@ func anyFlagProvided(kctx *kong.Context, names ...string) bool {
353353 return false
354354}
355355
356+ func flagValue [T any ](set bool , value T ) any {
357+ if ! set {
358+ return nil
359+ }
360+ return value
361+ }
362+
363+ func (c * ContactsUpdateCmd ) validateFlagUpdateInputs (wantBirthday , wantCustom , wantRelation bool ) error {
364+ if wantCustom {
365+ if _ , _ , err := parseCustomUserDefined (c .Custom , true ); err != nil {
366+ return usage (err .Error ())
367+ }
368+ }
369+ if wantRelation {
370+ if _ , _ , err := parseRelations (c .Relation , true ); err != nil {
371+ return usage (err .Error ())
372+ }
373+ }
374+ if wantBirthday && strings .TrimSpace (c .Birthday ) != "" {
375+ if _ , err := parseYYYYMMDD (strings .TrimSpace (c .Birthday )); err != nil {
376+ return usage ("invalid --birthday (expected YYYY-MM-DD)" )
377+ }
378+ }
379+ return nil
380+ }
381+
356382func (c * ContactsCreateCmd ) Run (ctx context.Context , flags * RootFlags ) error {
357383 u := ui .FromContext (ctx )
358- account , err := requireAccount (flags )
359- if err != nil {
360- return err
361- }
362384 if strings .TrimSpace (c .Given ) == "" {
363385 return usage ("required: --given" )
364386 }
365387
366- svc , err := newPeopleContactsService (ctx , account )
367- if err != nil {
368- return err
369- }
370-
371388 p := & people.Person {
372389 Names : []* people.Name {{
373390 GivenName : strings .TrimSpace (c .Given ),
@@ -421,6 +438,19 @@ func (c *ContactsCreateCmd) Run(ctx context.Context, flags *RootFlags) error {
421438 }
422439 }
423440
441+ if err := dryRunExit (ctx , flags , "contacts.create" , map [string ]any {"contact" : p }); err != nil {
442+ return err
443+ }
444+
445+ account , err := requireAccount (flags )
446+ if err != nil {
447+ return err
448+ }
449+ svc , err := newPeopleContactsService (ctx , account )
450+ if err != nil {
451+ return err
452+ }
453+
424454 created , err := svc .People .CreateContact (p ).Do ()
425455 if err != nil {
426456 return err
@@ -454,22 +484,55 @@ type ContactsUpdateCmd struct {
454484 Notes string `name:"notes" help:"Notes (stored as People API biography; empty clears)"`
455485}
456486
487+ type contactsUpdateFieldFlags struct {
488+ given bool
489+ family bool
490+ email bool
491+ phone bool
492+ org bool
493+ title bool
494+ url bool
495+ note bool
496+ address bool
497+ gender bool
498+ birthday bool
499+ notes bool
500+ custom bool
501+ relation bool
502+ }
503+
504+ func contactsUpdateFieldFlagsFromKong (kctx * kong.Context ) contactsUpdateFieldFlags {
505+ return contactsUpdateFieldFlags {
506+ given : flagProvided (kctx , "given" ),
507+ family : flagProvided (kctx , "family" ),
508+ email : flagProvided (kctx , "email" ),
509+ phone : flagProvided (kctx , "phone" ),
510+ org : flagProvided (kctx , "org" ),
511+ title : flagProvided (kctx , "title" ),
512+ url : flagProvided (kctx , "url" ),
513+ note : flagProvided (kctx , "note" ),
514+ address : flagProvided (kctx , "address" ),
515+ gender : flagProvided (kctx , "gender" ),
516+ birthday : flagProvided (kctx , "birthday" ),
517+ notes : flagProvided (kctx , "notes" ),
518+ custom : flagProvided (kctx , "custom" ),
519+ relation : flagProvided (kctx , "relation" ),
520+ }
521+ }
522+
523+ func (w contactsUpdateFieldFlags ) any () bool {
524+ return w .given || w .family || w .email || w .phone || w .org || w .title ||
525+ w .url || w .note || w .address || w .gender || w .birthday || w .notes ||
526+ w .custom || w .relation
527+ }
528+
457529func (c * ContactsUpdateCmd ) Run (ctx context.Context , kctx * kong.Context , flags * RootFlags ) error {
458530 u := ui .FromContext (ctx )
459- account , err := requireAccount (flags )
460- if err != nil {
461- return err
462- }
463531 resourceName := strings .TrimSpace (c .ResourceName )
464532 if ! strings .HasPrefix (resourceName , "people/" ) {
465533 return usage ("resourceName must start with people/" )
466534 }
467535
468- svc , err := newPeopleContactsService (ctx , account )
469- if err != nil {
470- return err
471- }
472-
473536 if strings .TrimSpace (c .FromFile ) != "" {
474537 if anyFlagProvided (kctx ,
475538 "given" , "family" , "email" , "phone" ,
@@ -479,56 +542,101 @@ func (c *ContactsUpdateCmd) Run(ctx context.Context, kctx *kong.Context, flags *
479542 ) {
480543 return usage ("can't combine --from-file with other update flags" )
481544 }
545+ if flags != nil && flags .DryRun {
546+ inputPerson , updateFields , err := c .readUpdateJSONInput (resourceName )
547+ if err != nil {
548+ return err
549+ }
550+ if err := dryRunExit (ctx , flags , "contacts.update" , map [string ]any {
551+ "resourceName" : resourceName ,
552+ "from_file" : strings .TrimSpace (c .FromFile ),
553+ "updateFields" : updateFields ,
554+ "contact" : inputPerson ,
555+ }); err != nil {
556+ return err
557+ }
558+ }
559+ account , err := requireAccount (flags )
560+ if err != nil {
561+ return err
562+ }
563+ svc , err := newPeopleContactsService (ctx , account )
564+ if err != nil {
565+ return err
566+ }
482567 return c .updateFromJSON (ctx , svc , resourceName , u )
483568 }
484569
570+ want := contactsUpdateFieldFlagsFromKong (kctx )
571+ if ! want .any () {
572+ return usage ("no updates provided" )
573+ }
574+ if err := c .validateFlagUpdateInputs (want .birthday , want .custom , want .relation ); err != nil {
575+ return err
576+ }
577+
578+ if err := dryRunExit (ctx , flags , "contacts.update" , map [string ]any {
579+ "resourceName" : resourceName ,
580+ "fields" : map [string ]any {
581+ "given" : flagValue (want .given , c .Given ),
582+ "family" : flagValue (want .family , c .Family ),
583+ "email" : flagValue (want .email , c .Email ),
584+ "phone" : flagValue (want .phone , c .Phone ),
585+ "organization" : flagValue (want .org , c .Organization ),
586+ "title" : flagValue (want .title , c .Title ),
587+ "url" : flagValue (want .url , c .URL ),
588+ "note" : flagValue (want .note , c .Note ),
589+ "address" : flagValue (want .address , c .Address ),
590+ "gender" : flagValue (want .gender , c .Gender ),
591+ "birthday" : flagValue (want .birthday , c .Birthday ),
592+ "notes" : flagValue (want .notes , c .Notes ),
593+ "custom" : flagValue (want .custom , c .Custom ),
594+ "relation" : flagValue (want .relation , c .Relation ),
595+ },
596+ }); err != nil {
597+ return err
598+ }
599+
600+ account , err := requireAccount (flags )
601+ if err != nil {
602+ return err
603+ }
604+ svc , err := newPeopleContactsService (ctx , account )
605+ if err != nil {
606+ return err
607+ }
485608 existing , err := svc .People .Get (resourceName ).PersonFields (contactsUpdateReadMask ).Do ()
486609 if err != nil {
487610 return err
488611 }
489612
490613 updateFields := make ([]string , 0 , 8 )
491614
492- wantGiven := flagProvided (kctx , "given" )
493- wantFamily := flagProvided (kctx , "family" )
494- wantEmail := flagProvided (kctx , "email" )
495- wantPhone := flagProvided (kctx , "phone" )
496- wantOrg := flagProvided (kctx , "org" )
497- wantTitle := flagProvided (kctx , "title" )
498- wantURL := flagProvided (kctx , "url" )
499- wantNote := flagProvided (kctx , "note" )
500- wantAddress := flagProvided (kctx , "address" )
501- wantGender := flagProvided (kctx , "gender" )
502- wantBirthday := flagProvided (kctx , "birthday" )
503- wantNotes := flagProvided (kctx , "notes" )
504- wantCustom := flagProvided (kctx , "custom" )
505- wantRelation := flagProvided (kctx , "relation" )
506-
507- if wantGiven || wantFamily {
508- contactsApplyPersonName (existing , wantGiven , c .Given , wantFamily , c .Family )
615+ if want .given || want .family {
616+ contactsApplyPersonName (existing , want .given , c .Given , want .family , c .Family )
509617 updateFields = append (updateFields , "names" )
510618 }
511- if wantEmail {
619+ if want . email {
512620 if strings .TrimSpace (c .Email ) == "" {
513621 existing .EmailAddresses = nil // will be forced to [] for patch
514622 } else {
515623 existing .EmailAddresses = []* people.EmailAddress {{Value : strings .TrimSpace (c .Email )}}
516624 }
517625 updateFields = append (updateFields , "emailAddresses" )
518626 }
519- if wantPhone {
627+ if want . phone {
520628 if strings .TrimSpace (c .Phone ) == "" {
521629 existing .PhoneNumbers = nil // will be forced to [] for patch
522630 } else {
523631 existing .PhoneNumbers = []* people.PhoneNumber {{Value : strings .TrimSpace (c .Phone )}}
524632 }
525633 updateFields = append (updateFields , "phoneNumbers" )
526634 }
527- if wantOrg || wantTitle {
528- contactsApplyPersonOrganization (existing , wantOrg , c .Organization , wantTitle , c .Title )
635+ if want . org || want . title {
636+ contactsApplyPersonOrganization (existing , want . org , c .Organization , want . title , c .Title )
529637 updateFields = append (updateFields , "organizations" )
530638 }
531- if wantURL {
639+ if want . url {
532640 urls := contactsURLs (c .URL )
533641 if len (urls ) == 0 {
534642 existing .Urls = nil
@@ -537,15 +645,15 @@ func (c *ContactsUpdateCmd) Run(ctx context.Context, kctx *kong.Context, flags *
537645 }
538646 updateFields = append (updateFields , "urls" )
539647 }
540- if wantNote {
648+ if want . note {
541649 if strings .TrimSpace (c .Note ) == "" {
542650 existing .Biographies = nil
543651 } else {
544652 existing .Biographies = []* people.Biography {{Value : strings .TrimSpace (c .Note )}}
545653 }
546654 updateFields = append (updateFields , "biographies" )
547655 }
548- if wantAddress {
656+ if want . address {
549657 addrs := contactsAddresses (c .Address )
550658 if len (addrs ) == 0 {
551659 existing .Addresses = nil // will be forced to [] for patch
@@ -554,7 +662,7 @@ func (c *ContactsUpdateCmd) Run(ctx context.Context, kctx *kong.Context, flags *
554662 }
555663 updateFields = append (updateFields , "addresses" )
556664 }
557- if wantGender {
665+ if want . gender {
558666 genders := contactsGenders (c .Gender )
559667 if len (genders ) == 0 {
560668 existing .Genders = nil // will be forced to [] for patch
@@ -563,7 +671,7 @@ func (c *ContactsUpdateCmd) Run(ctx context.Context, kctx *kong.Context, flags *
563671 }
564672 updateFields = append (updateFields , "genders" )
565673 }
566- if wantCustom {
674+ if want . custom {
567675 userDefined , clearAll , parseErr := parseCustomUserDefined (c .Custom , true )
568676 if parseErr != nil {
569677 return usage (parseErr .Error ())
@@ -575,7 +683,7 @@ func (c *ContactsUpdateCmd) Run(ctx context.Context, kctx *kong.Context, flags *
575683 }
576684 updateFields = append (updateFields , "userDefined" )
577685 }
578- if wantRelation {
686+ if want . relation {
579687 relations , clearAll , parseErr := parseRelations (c .Relation , true )
580688 if parseErr != nil {
581689 return usage (parseErr .Error ())
@@ -588,7 +696,7 @@ func (c *ContactsUpdateCmd) Run(ctx context.Context, kctx *kong.Context, flags *
588696 updateFields = append (updateFields , "relations" )
589697 }
590698
591- if wantBirthday {
699+ if want . birthday {
592700 if strings .TrimSpace (c .Birthday ) == "" {
593701 existing .Birthdays = nil // will be forced to [] for patch
594702 } else {
@@ -604,7 +712,7 @@ func (c *ContactsUpdateCmd) Run(ctx context.Context, kctx *kong.Context, flags *
604712 updateFields = append (updateFields , "birthdays" )
605713 }
606714
607- if wantNotes {
715+ if want . notes {
608716 if strings .TrimSpace (c .Notes ) == "" {
609717 existing .Biographies = nil // will be forced to [] for patch
610718 } else {
0 commit comments