mbrt/gmailctl

Easier-to-parse diff

lutzky opened this issue · 11 comments

Currently gmailctl diff can sometimes show output like this:

- to: {long@gmail.com list@gmail.com of@gmail.com aliases@gmail.com that@gmail.com apply@gmail.com to@gmail.com this@gmail.com rule@gmail.com}
+ to: {long@gmail.com list@gmail.com of@gmail.com aliases@gmail.com that@gmail.com apply@gmail.com this@gmail.com rule@gmali.com}

...it's not fun figuring out what's missing there.

The cmp package does what I mean, I think: https://go.dev/play/p/E5RwmDYIsvX -

  main.WithSlices{
  	To: []string{
  		... // 4 identical elements
  		"that@gmail.com",
  		"apply@gmail.com",
- 		"to@gmail.com",
  		"this@gmail.com",
  		"rule@gmail.com",
  	},
  }
mbrt commented

This is not easy to do, because the diff is done at the lowest level of filters, where all the fields are simple strings. This allows for easy comparison with whatever is currently set in Gmail, without having to parse and back-translate upstream filters to higher level representations.

The line you report (to: {long@gmail.comt .... }) is effectively just a string, so the cmp package would report the same result.

Yep, my process definitely started with "I'll just send a pull request" and quickly turned into "this is nontrivial and should be a feature request".

Is there a reasonable place to say "here are all the filters before, here are all the filters after, throw it at cmp.Diff and see how bad it looks"? Should serve as a reasonable starting point.

Alright, I found a place and ran it, lutzky@f3264ab. The output doesn't look great.

mbrt commented

Yeah indeed. It's a bunch of strings not very different from what's existing. To make it better you really need a parser for Gmail filter expressions.

Yep. Now, even if I were to write such a parser, the output for cmp.Diff on imagined parsed filters doesn't look great:

 main.FilterNode{
  	And: []main.FilterNode{
  		{
  			And: nil,
  			Or: []main.FilterNode{
  				{From: "person1"},
  				{From: "person2"},
- 				{From: "person3"},
  				{From: "person4"},
  			},
  			Not:  nil,
  			From: "",
  		},
  		{
  			And: nil,
  			Or:  nil,
  			Not: &main.FilterNode{
  				And: nil,
  				Or: []main.FilterNode{
  					{From: "person5"},
  					{
  						And:  nil,
  						Or:   nil,
  						Not:  nil,
- 						From: "person6",
+ 						From: "person7",
  					},
  				},
  				Not:  nil,
  				From: "",
  			},
  			From: "",
  		},
  	},
  	Or:   nil,
  	Not:  nil,
  	From: "",
  }

Even if we hide all the nil and "" entries, what we actually want is more like this:

before: {person1 person2 person3 person4} -{person5 person6}
after:  {person1 person2 person4} -{person5 person7}
diff:
 {
   person1
   person2
-  person3
   person4
 }
 -{
   person5
-  person6
+  person7
  }

So, essentially, it's a matter of adding newlines and indentation in a few places, and running a traditional diff. I'll see if I can mock something up.

It's quick-and-dirty, but perhaps not entirely useless: https://go.dev/play/p/EREXFY8gTwR

mbrt commented

It's certainly a start. There are a lot more cases to consider though. From the top of my head:

  • quotes: from:"foo bar"
  • parenthesis: (a b) subject:(foo bar)
  • nested expressions: -(x {y -z} k)

And combinations of those.

This issue is stale because it has been open for 30 days without activity.
This will be closed in 7 days, unless you add the 'lifecycle/keep-alive' label or comment.

Tweaked it a bit to handle your example cases: https://go.dev/play/p/qNFzmtFEWCo

WDYT?

mbrt commented

Cool! I tried a couple more nasty cases (colons outside operators) and the results still seem good! https://go.dev/play/p/LtIy47e5eY7.