diff --git a/go.mod b/go.mod index 33e1828..c121cb4 100644 --- a/go.mod +++ b/go.mod @@ -2,26 +2,31 @@ module somegit.dev/vikingowl/reddit-reader go 1.26 +require ( + github.com/pelletier/go-toml/v2 v2.3.0 + github.com/spf13/cobra v1.10.2 + github.com/vartanbeno/go-reddit/v2 v2.0.1 + google.golang.org/grpc v1.80.0 + google.golang.org/protobuf v1.36.11 + modernc.org/sqlite v1.48.0 + somegit.dev/vikingowl/mistral-go-sdk v1.2.0 +) + require ( github.com/dustin/go-humanize v1.0.1 // indirect - github.com/golang/protobuf v1.2.0 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect - github.com/pelletier/go-toml/v2 v2.3.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.9 // indirect - github.com/vartanbeno/go-reddit/v2 v2.0.1 // indirect - golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e // indirect - golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect golang.org/x/sys v0.42.0 // indirect - google.golang.org/appengine v1.4.0 // indirect + golang.org/x/text v0.33.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect modernc.org/libc v1.70.0 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect - modernc.org/sqlite v1.48.0 // indirect - somegit.dev/vikingowl/mistral-go-sdk v1.2.0 // indirect ) diff --git a/go.sum b/go.sum index 3bde7f7..046c47d 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,28 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -17,6 +31,7 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM= github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= @@ -26,31 +41,82 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/vartanbeno/go-reddit/v2 v2.0.1 h1:P6ITpf5YHjdy7DHZIbUIDn/iNAoGcEoDQnMa+L4vutw= github.com/vartanbeno/go-reddit/v2 v2.0.1/go.mod h1:758/S10hwZSLm43NPtwoNQdZFSg3sjB5745Mwjb0ANI= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= +modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw= +modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0= +modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= +modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= +modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw= modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sqlite v1.48.0 h1:ElZyLop3Q2mHYk5IFPPXADejZrlHu7APbpB0sF78bq4= modernc.org/sqlite v1.48.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= somegit.dev/vikingowl/mistral-go-sdk v1.2.0 h1:9NEGCKzw1Bu2c8LaSEKNlpj08iMsU0fkDFJO6W1Zh+Y= somegit.dev/vikingowl/mistral-go-sdk v1.2.0/go.mod h1:pN7nQdOIYYEMRdwye5cSfymtwhZJHd+caK6J69Z4XMY= diff --git a/internal/grpc/client/client.go b/internal/grpc/client/client.go new file mode 100644 index 0000000..b7a7c77 --- /dev/null +++ b/internal/grpc/client/client.go @@ -0,0 +1,190 @@ +package client + +import ( + "context" + "fmt" + "io" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "somegit.dev/vikingowl/reddit-reader/internal/domain" + pb "somegit.dev/vikingowl/reddit-reader/proto/redditreader" +) + +// Client wraps a gRPC connection to the RedditReader service. +type Client struct { + conn *grpc.ClientConn + client pb.RedditReaderClient +} + +// Dial connects to the gRPC server via a Unix socket. +func Dial(socketPath string) (*Client, error) { + conn, err := grpc.NewClient( + "unix://"+socketPath, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + return nil, fmt.Errorf("grpc dial: %w", err) + } + return &Client{ + conn: conn, + client: pb.NewRedditReaderClient(conn), + }, nil +} + +// Close closes the underlying gRPC connection. +func (c *Client) Close() error { + return c.conn.Close() +} + +// ListPosts retrieves posts matching the given subreddit and limit. +func (c *Client) ListPosts(ctx context.Context, subreddit string, limit int) ([]domain.Post, error) { + resp, err := c.client.ListPosts(ctx, &pb.ListRequest{ + Subreddit: subreddit, + Limit: int32(limit), + }) + if err != nil { + return nil, fmt.Errorf("list posts: %w", err) + } + posts := make([]domain.Post, len(resp.GetPosts())) + for i, p := range resp.GetPosts() { + posts[i] = protoToDomain(p) + } + return posts, nil +} + +// UpdatePost updates flags on a post and returns the updated version. +func (c *Client) UpdatePost(ctx context.Context, id string, read, starred, dismissed *bool) (domain.Post, error) { + req := &pb.UpdateRequest{Id: id} + if read != nil { + req.Read = read + } + if starred != nil { + req.Starred = starred + } + if dismissed != nil { + req.Dismissed = dismissed + } + resp, err := c.client.UpdatePost(ctx, req) + if err != nil { + return domain.Post{}, fmt.Errorf("update post: %w", err) + } + return protoToDomain(resp), nil +} + +// SubmitFeedback records a vote for a post. +func (c *Client) SubmitFeedback(ctx context.Context, postID string, vote int) error { + _, err := c.client.SubmitFeedback(ctx, &pb.FeedbackRequest{ + PostId: postID, + Vote: int32(vote), + }) + if err != nil { + return fmt.Errorf("submit feedback: %w", err) + } + return nil +} + +// ListSubreddits returns all configured subreddits. +func (c *Client) ListSubreddits(ctx context.Context) ([]domain.Subreddit, error) { + resp, err := c.client.ListSubreddits(ctx, &pb.Empty{}) + if err != nil { + return nil, fmt.Errorf("list subreddits: %w", err) + } + subs := make([]domain.Subreddit, len(resp.GetSubreddits())) + for i, s := range resp.GetSubreddits() { + subs[i] = domain.Subreddit{ + Name: s.GetName(), + Enabled: s.GetEnabled(), + PollSort: s.GetPollSort(), + } + } + return subs, nil +} + +// AddSubreddit adds a subreddit with the given sort order. +func (c *Client) AddSubreddit(ctx context.Context, name, sort string) error { + _, err := c.client.AddSubreddit(ctx, &pb.AddSubredditRequest{ + Name: name, + PollSort: sort, + }) + if err != nil { + return fmt.Errorf("add subreddit: %w", err) + } + return nil +} + +// RemoveSubreddit deletes a subreddit by name. +func (c *Client) RemoveSubreddit(ctx context.Context, name string) error { + _, err := c.client.RemoveSubreddit(ctx, &pb.RemoveRequest{Name: name}) + if err != nil { + return fmt.Errorf("remove subreddit: %w", err) + } + return nil +} + +// StreamPosts returns a channel that receives posts as they are pushed by the server. +func (c *Client) StreamPosts(ctx context.Context) (<-chan domain.Post, error) { + stream, err := c.client.StreamPosts(ctx, &pb.StreamRequest{}) + if err != nil { + return nil, fmt.Errorf("stream posts: %w", err) + } + ch := make(chan domain.Post, 64) + go func() { + defer close(ch) + for { + p, err := stream.Recv() + if err == io.EOF { + return + } + if err != nil { + return + } + select { + case ch <- protoToDomain(p): + case <-ctx.Done(): + return + } + } + }() + return ch, nil +} + +// Status returns the server's status information. +func (c *Client) Status(ctx context.Context) (*pb.StatusResponse, error) { + resp, err := c.client.Status(ctx, &pb.Empty{}) + if err != nil { + return nil, fmt.Errorf("status: %w", err) + } + return resp, nil +} + +func protoToDomain(p *pb.Post) domain.Post { + post := domain.Post{ + ID: p.GetId(), + Subreddit: p.GetSubreddit(), + Title: p.GetTitle(), + Author: p.GetAuthor(), + URL: p.GetUrl(), + SelfText: p.GetSelfText(), + Score: int(p.GetScore()), + Read: p.GetRead(), + Starred: p.GetStarred(), + Dismissed: p.GetDismissed(), + } + if ts := p.GetCreatedUtc(); ts != nil { + post.CreatedUTC = ts.AsTime() + } + if ts := p.GetFetchedAt(); ts != nil { + post.FetchedAt = ts.AsTime() + } + if p.Relevance != nil { + v := p.GetRelevance() + post.Relevance = &v + } + if p.Summary != nil { + v := p.GetSummary() + post.Summary = &v + } + return post +} + diff --git a/internal/grpc/server/server.go b/internal/grpc/server/server.go new file mode 100644 index 0000000..9821590 --- /dev/null +++ b/internal/grpc/server/server.go @@ -0,0 +1,265 @@ +package server + +import ( + "context" + "sync" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/timestamppb" + + "somegit.dev/vikingowl/reddit-reader/internal/domain" + "somegit.dev/vikingowl/reddit-reader/internal/store" + pb "somegit.dev/vikingowl/reddit-reader/proto/redditreader" +) + +// Server implements the RedditReader gRPC service. +type Server struct { + pb.UnimplementedRedditReaderServer + store *store.Store + startedAt time.Time + mu sync.RWMutex + subscribers map[chan *pb.Post]struct{} +} + +// Register creates a Server and registers it with the gRPC server. +func Register(srv *grpc.Server, st *store.Store, startedAt time.Time) *Server { + s := &Server{ + store: st, + startedAt: startedAt, + subscribers: make(map[chan *pb.Post]struct{}), + } + pb.RegisterRedditReaderServer(srv, s) + return s +} + +// Notify pushes new posts to all connected stream subscribers. +func (s *Server) Notify(posts []domain.Post) { + s.mu.RLock() + defer s.mu.RUnlock() + for ch := range s.subscribers { + for i := range posts { + p := domainToProto(posts[i]) + select { + case ch <- p: + default: + // subscriber too slow, drop + } + } + } +} + +// StreamPosts adds a subscriber channel and sends posts as they arrive. +func (s *Server) StreamPosts(_ *pb.StreamRequest, stream grpc.ServerStreamingServer[pb.Post]) error { + ch := make(chan *pb.Post, 64) + + s.mu.Lock() + s.subscribers[ch] = struct{}{} + s.mu.Unlock() + + defer func() { + s.mu.Lock() + delete(s.subscribers, ch) + s.mu.Unlock() + }() + + for { + select { + case <-stream.Context().Done(): + return stream.Context().Err() + case post := <-ch: + if err := stream.Send(post); err != nil { + return err + } + } + } +} + +// ListPosts returns posts matching the filter criteria. +func (s *Server) ListPosts(_ context.Context, req *pb.ListRequest) (*pb.ListResponse, error) { + f := store.ListFilter{ + Subreddit: req.GetSubreddit(), + Limit: int(req.GetLimit()), + } + if req.Unread != nil { + v := req.GetUnread() + f.Unread = &v + } + if req.Starred != nil { + v := req.GetStarred() + f.Starred = &v + } + if req.Dismissed != nil { + v := req.GetDismissed() + f.Dismissed = &v + } + + posts, err := s.store.ListPosts(f) + if err != nil { + return nil, status.Errorf(codes.Internal, "list posts: %v", err) + } + + resp := &pb.ListResponse{Posts: make([]*pb.Post, len(posts))} + for i := range posts { + resp.Posts[i] = domainToProto(posts[i]) + } + return resp, nil +} + +// UpdatePost updates flags on a post and returns the updated version. +func (s *Server) UpdatePost(_ context.Context, req *pb.UpdateRequest) (*pb.Post, error) { + if req.GetId() == "" { + return nil, status.Error(codes.InvalidArgument, "id is required") + } + + u := store.PostUpdate{} + if req.Read != nil { + v := req.GetRead() + u.Read = &v + } + if req.Starred != nil { + v := req.GetStarred() + u.Starred = &v + } + if req.Dismissed != nil { + v := req.GetDismissed() + u.Dismissed = &v + } + + if err := s.store.UpdatePost(req.GetId(), u); err != nil { + return nil, status.Errorf(codes.Internal, "update post: %v", err) + } + + post, err := s.store.GetPost(req.GetId()) + if err != nil { + return nil, status.Errorf(codes.NotFound, "post %q not found", req.GetId()) + } + return domainToProto(post), nil +} + +// SubmitFeedback records a vote for a post. +func (s *Server) SubmitFeedback(_ context.Context, req *pb.FeedbackRequest) (*pb.FeedbackResponse, error) { + if err := s.store.AddFeedback(req.GetPostId(), int(req.GetVote())); err != nil { + return nil, status.Errorf(codes.Internal, "add feedback: %v", err) + } + return &pb.FeedbackResponse{}, nil +} + +// ListSubreddits returns all configured subreddits. +func (s *Server) ListSubreddits(_ context.Context, _ *pb.Empty) (*pb.SubredditList, error) { + subs, err := s.store.ListSubreddits() + if err != nil { + return nil, status.Errorf(codes.Internal, "list subreddits: %v", err) + } + + resp := &pb.SubredditList{Subreddits: make([]*pb.SubredditMsg, len(subs))} + for i, sub := range subs { + resp.Subreddits[i] = &pb.SubredditMsg{ + Name: sub.Name, + Enabled: sub.Enabled, + PollSort: sub.PollSort, + } + } + return resp, nil +} + +// AddSubreddit adds a subreddit and returns it. +func (s *Server) AddSubreddit(_ context.Context, req *pb.AddSubredditRequest) (*pb.SubredditMsg, error) { + sub := domain.Subreddit{ + Name: req.GetName(), + PollSort: req.GetPollSort(), + } + if err := s.store.AddSubreddit(sub); err != nil { + return nil, status.Errorf(codes.Internal, "add subreddit: %v", err) + } + return &pb.SubredditMsg{ + Name: sub.Name, + Enabled: true, + PollSort: sub.PollSort, + }, nil +} + +// RemoveSubreddit deletes a subreddit by name. +func (s *Server) RemoveSubreddit(_ context.Context, req *pb.RemoveRequest) (*pb.Empty, error) { + if err := s.store.RemoveSubreddit(req.GetName()); err != nil { + return nil, status.Errorf(codes.Internal, "remove subreddit: %v", err) + } + return &pb.Empty{}, nil +} + +// UpdateFilters adds the provided filters for a subreddit and returns all filters. +func (s *Server) UpdateFilters(_ context.Context, req *pb.FilterRequest) (*pb.FilterResponse, error) { + for _, f := range req.GetFilters() { + _, err := s.store.AddFilter(domain.Filter{ + Subreddit: req.GetSubreddit(), + Pattern: f.GetPattern(), + IsRegex: f.GetIsRegex(), + }) + if err != nil { + return nil, status.Errorf(codes.Internal, "add filter: %v", err) + } + } + + filters, err := s.store.ListFilters(req.GetSubreddit()) + if err != nil { + return nil, status.Errorf(codes.Internal, "list filters: %v", err) + } + + resp := &pb.FilterResponse{Filters: make([]*pb.FilterMsg, len(filters))} + for i, f := range filters { + resp.Filters[i] = &pb.FilterMsg{ + Id: f.ID, + Subreddit: f.Subreddit, + Pattern: f.Pattern, + IsRegex: f.IsRegex, + } + } + return resp, nil +} + +// Status returns uptime and post counts. +func (s *Server) Status(_ context.Context, _ *pb.Empty) (*pb.StatusResponse, error) { + allPosts, err := s.store.ListPosts(store.ListFilter{}) + if err != nil { + return nil, status.Errorf(codes.Internal, "count posts: %v", err) + } + + unread := 0 + for _, p := range allPosts { + if !p.Read { + unread++ + } + } + + return &pb.StatusResponse{ + UptimeSeconds: int64(time.Since(s.startedAt).Seconds()), + TotalPosts: int32(len(allPosts)), + UnreadPosts: int32(unread), + }, nil +} + +func domainToProto(p domain.Post) *pb.Post { + out := &pb.Post{ + Id: p.ID, + Subreddit: p.Subreddit, + Title: p.Title, + Author: p.Author, + Url: p.URL, + SelfText: p.SelfText, + Score: int32(p.Score), + CreatedUtc: timestamppb.New(p.CreatedUTC), + FetchedAt: timestamppb.New(p.FetchedAt), + Read: p.Read, + Starred: p.Starred, + Dismissed: p.Dismissed, + } + if p.Relevance != nil { + out.Relevance = p.Relevance + } + if p.Summary != nil { + out.Summary = p.Summary + } + return out +} diff --git a/internal/grpc/server/server_test.go b/internal/grpc/server/server_test.go new file mode 100644 index 0000000..9f79bcc --- /dev/null +++ b/internal/grpc/server/server_test.go @@ -0,0 +1,246 @@ +package server_test + +import ( + "context" + "net" + "testing" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "somegit.dev/vikingowl/reddit-reader/internal/domain" + grpcserver "somegit.dev/vikingowl/reddit-reader/internal/grpc/server" + "somegit.dev/vikingowl/reddit-reader/internal/store" + pb "somegit.dev/vikingowl/reddit-reader/proto/redditreader" +) + +func setupTestServer(t *testing.T) (pb.RedditReaderClient, *store.Store) { + t.Helper() + st, err := store.Open(":memory:") + if err != nil { + t.Fatalf("store.Open: %v", err) + } + t.Cleanup(func() { st.Close() }) + + lis, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("net.Listen: %v", err) + } + srv := grpc.NewServer() + grpcserver.Register(srv, st, time.Now()) + go srv.Serve(lis) + t.Cleanup(func() { srv.GracefulStop() }) + + conn, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + t.Fatalf("grpc.NewClient: %v", err) + } + t.Cleanup(func() { conn.Close() }) + + return pb.NewRedditReaderClient(conn), st +} + +func TestListPostsEmpty(t *testing.T) { + client, _ := setupTestServer(t) + resp, err := client.ListPosts(context.Background(), &pb.ListRequest{}) + if err != nil { + t.Fatalf("ListPosts: %v", err) + } + if len(resp.Posts) != 0 { + t.Errorf("expected 0 posts, got %d", len(resp.Posts)) + } +} + +func TestListPostsWithData(t *testing.T) { + client, st := setupTestServer(t) + rel := 0.8 + if err := st.InsertPost(domain.Post{ + ID: "t3_a", + Subreddit: "golang", + Title: "Test", + CreatedUTC: time.Now(), + Relevance: &rel, + }); err != nil { + t.Fatalf("InsertPost: %v", err) + } + + resp, err := client.ListPosts(context.Background(), &pb.ListRequest{}) + if err != nil { + t.Fatalf("ListPosts: %v", err) + } + if len(resp.Posts) != 1 { + t.Fatalf("expected 1 post, got %d", len(resp.Posts)) + } + if resp.Posts[0].Title != "Test" { + t.Errorf("Title = %q, want Test", resp.Posts[0].Title) + } +} + +func TestUpdatePost(t *testing.T) { + client, st := setupTestServer(t) + if err := st.InsertPost(domain.Post{ + ID: "t3_a", + Subreddit: "test", + Title: "Test", + CreatedUTC: time.Now(), + }); err != nil { + t.Fatalf("InsertPost: %v", err) + } + + starred := true + resp, err := client.UpdatePost(context.Background(), &pb.UpdateRequest{ + Id: "t3_a", + Starred: &starred, + }) + if err != nil { + t.Fatalf("UpdatePost: %v", err) + } + if !resp.Starred { + t.Error("expected starred") + } +} + +func TestSubmitFeedback(t *testing.T) { + client, st := setupTestServer(t) + if err := st.InsertPost(domain.Post{ + ID: "t3_a", + Subreddit: "test", + Title: "Test", + CreatedUTC: time.Now(), + }); err != nil { + t.Fatalf("InsertPost: %v", err) + } + + _, err := client.SubmitFeedback(context.Background(), &pb.FeedbackRequest{ + PostId: "t3_a", + Vote: 1, + }) + if err != nil { + t.Fatalf("SubmitFeedback: %v", err) + } + + fb, err := st.RecentFeedback(10) + if err != nil { + t.Fatalf("RecentFeedback: %v", err) + } + if len(fb) != 1 || fb[0].Vote != 1 { + t.Errorf("feedback = %v", fb) + } +} + +func TestSubredditCRUD(t *testing.T) { + client, _ := setupTestServer(t) + + _, err := client.AddSubreddit(context.Background(), &pb.AddSubredditRequest{ + Name: "golang", + PollSort: "new", + }) + if err != nil { + t.Fatalf("AddSubreddit: %v", err) + } + + list, err := client.ListSubreddits(context.Background(), &pb.Empty{}) + if err != nil { + t.Fatalf("ListSubreddits: %v", err) + } + if len(list.Subreddits) != 1 { + t.Fatalf("expected 1 sub, got %d", len(list.Subreddits)) + } + + _, err = client.RemoveSubreddit(context.Background(), &pb.RemoveRequest{Name: "golang"}) + if err != nil { + t.Fatalf("RemoveSubreddit: %v", err) + } + + list, err = client.ListSubreddits(context.Background(), &pb.Empty{}) + if err != nil { + t.Fatalf("ListSubreddits after remove: %v", err) + } + if len(list.Subreddits) != 0 { + t.Errorf("expected 0 after remove, got %d", len(list.Subreddits)) + } +} + +func TestStatus(t *testing.T) { + client, _ := setupTestServer(t) + resp, err := client.Status(context.Background(), &pb.Empty{}) + if err != nil { + t.Fatalf("Status: %v", err) + } + if resp.UptimeSeconds < 0 { + t.Error("uptime should be >= 0") + } +} + +func TestUpdateFilters(t *testing.T) { + client, st := setupTestServer(t) + + // Need to add the subreddit first (foreign key constraint). + if err := st.AddSubreddit(domain.Subreddit{Name: "golang", PollSort: "new"}); err != nil { + t.Fatalf("AddSubreddit: %v", err) + } + + resp, err := client.UpdateFilters(context.Background(), &pb.FilterRequest{ + Subreddit: "golang", + Filters: []*pb.FilterMsg{ + {Pattern: "hiring", IsRegex: false}, + }, + }) + if err != nil { + t.Fatalf("UpdateFilters: %v", err) + } + if len(resp.Filters) != 1 { + t.Fatalf("expected 1 filter, got %d", len(resp.Filters)) + } + if resp.Filters[0].Pattern != "hiring" { + t.Errorf("Pattern = %q, want hiring", resp.Filters[0].Pattern) + } +} + +func TestStreamNotify(t *testing.T) { + st, err := store.Open(":memory:") + if err != nil { + t.Fatalf("store.Open: %v", err) + } + t.Cleanup(func() { st.Close() }) + + lis, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("net.Listen: %v", err) + } + srv := grpc.NewServer() + s := grpcserver.Register(srv, st, time.Now()) + go srv.Serve(lis) + t.Cleanup(func() { srv.GracefulStop() }) + + conn, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + t.Fatalf("grpc.NewClient: %v", err) + } + t.Cleanup(func() { conn.Close() }) + + client := pb.NewRedditReaderClient(conn) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + stream, err := client.StreamPosts(ctx, &pb.StreamRequest{}) + if err != nil { + t.Fatalf("StreamPosts: %v", err) + } + + // Give the stream a moment to register the subscriber. + time.Sleep(100 * time.Millisecond) + + s.Notify([]domain.Post{ + {ID: "t3_stream", Subreddit: "test", Title: "Streamed", CreatedUTC: time.Now()}, + }) + + post, err := stream.Recv() + if err != nil { + t.Fatalf("Recv: %v", err) + } + if post.Title != "Streamed" { + t.Errorf("Title = %q, want Streamed", post.Title) + } +} diff --git a/proto/redditreader/redditreader.pb.go b/proto/redditreader/redditreader.pb.go new file mode 100644 index 0000000..3d8b416 --- /dev/null +++ b/proto/redditreader/redditreader.pb.go @@ -0,0 +1,1139 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v6.33.1 +// source: redditreader.proto + +package redditreader + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Post struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Subreddit string `protobuf:"bytes,2,opt,name=subreddit,proto3" json:"subreddit,omitempty"` + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` + Author string `protobuf:"bytes,4,opt,name=author,proto3" json:"author,omitempty"` + Url string `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"` + SelfText string `protobuf:"bytes,6,opt,name=self_text,json=selfText,proto3" json:"self_text,omitempty"` + Score int32 `protobuf:"varint,7,opt,name=score,proto3" json:"score,omitempty"` + CreatedUtc *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=created_utc,json=createdUtc,proto3" json:"created_utc,omitempty"` + FetchedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=fetched_at,json=fetchedAt,proto3" json:"fetched_at,omitempty"` + Relevance *float64 `protobuf:"fixed64,10,opt,name=relevance,proto3,oneof" json:"relevance,omitempty"` + Summary *string `protobuf:"bytes,11,opt,name=summary,proto3,oneof" json:"summary,omitempty"` + Read bool `protobuf:"varint,12,opt,name=read,proto3" json:"read,omitempty"` + Starred bool `protobuf:"varint,13,opt,name=starred,proto3" json:"starred,omitempty"` + Dismissed bool `protobuf:"varint,14,opt,name=dismissed,proto3" json:"dismissed,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Post) Reset() { + *x = Post{} + mi := &file_redditreader_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Post) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Post) ProtoMessage() {} + +func (x *Post) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Post.ProtoReflect.Descriptor instead. +func (*Post) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{0} +} + +func (x *Post) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Post) GetSubreddit() string { + if x != nil { + return x.Subreddit + } + return "" +} + +func (x *Post) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *Post) GetAuthor() string { + if x != nil { + return x.Author + } + return "" +} + +func (x *Post) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *Post) GetSelfText() string { + if x != nil { + return x.SelfText + } + return "" +} + +func (x *Post) GetScore() int32 { + if x != nil { + return x.Score + } + return 0 +} + +func (x *Post) GetCreatedUtc() *timestamppb.Timestamp { + if x != nil { + return x.CreatedUtc + } + return nil +} + +func (x *Post) GetFetchedAt() *timestamppb.Timestamp { + if x != nil { + return x.FetchedAt + } + return nil +} + +func (x *Post) GetRelevance() float64 { + if x != nil && x.Relevance != nil { + return *x.Relevance + } + return 0 +} + +func (x *Post) GetSummary() string { + if x != nil && x.Summary != nil { + return *x.Summary + } + return "" +} + +func (x *Post) GetRead() bool { + if x != nil { + return x.Read + } + return false +} + +func (x *Post) GetStarred() bool { + if x != nil { + return x.Starred + } + return false +} + +func (x *Post) GetDismissed() bool { + if x != nil { + return x.Dismissed + } + return false +} + +type StreamRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamRequest) Reset() { + *x = StreamRequest{} + mi := &file_redditreader_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamRequest) ProtoMessage() {} + +func (x *StreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamRequest.ProtoReflect.Descriptor instead. +func (*StreamRequest) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{1} +} + +type ListRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Subreddit string `protobuf:"bytes,1,opt,name=subreddit,proto3" json:"subreddit,omitempty"` + Unread *bool `protobuf:"varint,2,opt,name=unread,proto3,oneof" json:"unread,omitempty"` + Starred *bool `protobuf:"varint,3,opt,name=starred,proto3,oneof" json:"starred,omitempty"` + Dismissed *bool `protobuf:"varint,4,opt,name=dismissed,proto3,oneof" json:"dismissed,omitempty"` + Limit int32 `protobuf:"varint,5,opt,name=limit,proto3" json:"limit,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListRequest) Reset() { + *x = ListRequest{} + mi := &file_redditreader_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListRequest) ProtoMessage() {} + +func (x *ListRequest) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListRequest.ProtoReflect.Descriptor instead. +func (*ListRequest) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{2} +} + +func (x *ListRequest) GetSubreddit() string { + if x != nil { + return x.Subreddit + } + return "" +} + +func (x *ListRequest) GetUnread() bool { + if x != nil && x.Unread != nil { + return *x.Unread + } + return false +} + +func (x *ListRequest) GetStarred() bool { + if x != nil && x.Starred != nil { + return *x.Starred + } + return false +} + +func (x *ListRequest) GetDismissed() bool { + if x != nil && x.Dismissed != nil { + return *x.Dismissed + } + return false +} + +func (x *ListRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type ListResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Posts []*Post `protobuf:"bytes,1,rep,name=posts,proto3" json:"posts,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListResponse) Reset() { + *x = ListResponse{} + mi := &file_redditreader_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListResponse) ProtoMessage() {} + +func (x *ListResponse) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListResponse.ProtoReflect.Descriptor instead. +func (*ListResponse) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{3} +} + +func (x *ListResponse) GetPosts() []*Post { + if x != nil { + return x.Posts + } + return nil +} + +type UpdateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Read *bool `protobuf:"varint,2,opt,name=read,proto3,oneof" json:"read,omitempty"` + Starred *bool `protobuf:"varint,3,opt,name=starred,proto3,oneof" json:"starred,omitempty"` + Dismissed *bool `protobuf:"varint,4,opt,name=dismissed,proto3,oneof" json:"dismissed,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateRequest) Reset() { + *x = UpdateRequest{} + mi := &file_redditreader_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateRequest) ProtoMessage() {} + +func (x *UpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateRequest.ProtoReflect.Descriptor instead. +func (*UpdateRequest) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{4} +} + +func (x *UpdateRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateRequest) GetRead() bool { + if x != nil && x.Read != nil { + return *x.Read + } + return false +} + +func (x *UpdateRequest) GetStarred() bool { + if x != nil && x.Starred != nil { + return *x.Starred + } + return false +} + +func (x *UpdateRequest) GetDismissed() bool { + if x != nil && x.Dismissed != nil { + return *x.Dismissed + } + return false +} + +type FeedbackRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + PostId string `protobuf:"bytes,1,opt,name=post_id,json=postId,proto3" json:"post_id,omitempty"` + Vote int32 `protobuf:"varint,2,opt,name=vote,proto3" json:"vote,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FeedbackRequest) Reset() { + *x = FeedbackRequest{} + mi := &file_redditreader_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FeedbackRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FeedbackRequest) ProtoMessage() {} + +func (x *FeedbackRequest) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FeedbackRequest.ProtoReflect.Descriptor instead. +func (*FeedbackRequest) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{5} +} + +func (x *FeedbackRequest) GetPostId() string { + if x != nil { + return x.PostId + } + return "" +} + +func (x *FeedbackRequest) GetVote() int32 { + if x != nil { + return x.Vote + } + return 0 +} + +type FeedbackResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FeedbackResponse) Reset() { + *x = FeedbackResponse{} + mi := &file_redditreader_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FeedbackResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FeedbackResponse) ProtoMessage() {} + +func (x *FeedbackResponse) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FeedbackResponse.ProtoReflect.Descriptor instead. +func (*FeedbackResponse) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{6} +} + +type SubredditMsg struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` + PollSort string `protobuf:"bytes,3,opt,name=poll_sort,json=pollSort,proto3" json:"poll_sort,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubredditMsg) Reset() { + *x = SubredditMsg{} + mi := &file_redditreader_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubredditMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubredditMsg) ProtoMessage() {} + +func (x *SubredditMsg) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubredditMsg.ProtoReflect.Descriptor instead. +func (*SubredditMsg) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{7} +} + +func (x *SubredditMsg) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *SubredditMsg) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *SubredditMsg) GetPollSort() string { + if x != nil { + return x.PollSort + } + return "" +} + +type SubredditList struct { + state protoimpl.MessageState `protogen:"open.v1"` + Subreddits []*SubredditMsg `protobuf:"bytes,1,rep,name=subreddits,proto3" json:"subreddits,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubredditList) Reset() { + *x = SubredditList{} + mi := &file_redditreader_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubredditList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubredditList) ProtoMessage() {} + +func (x *SubredditList) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubredditList.ProtoReflect.Descriptor instead. +func (*SubredditList) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{8} +} + +func (x *SubredditList) GetSubreddits() []*SubredditMsg { + if x != nil { + return x.Subreddits + } + return nil +} + +type AddSubredditRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + PollSort string `protobuf:"bytes,2,opt,name=poll_sort,json=pollSort,proto3" json:"poll_sort,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AddSubredditRequest) Reset() { + *x = AddSubredditRequest{} + mi := &file_redditreader_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddSubredditRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddSubredditRequest) ProtoMessage() {} + +func (x *AddSubredditRequest) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddSubredditRequest.ProtoReflect.Descriptor instead. +func (*AddSubredditRequest) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{9} +} + +func (x *AddSubredditRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AddSubredditRequest) GetPollSort() string { + if x != nil { + return x.PollSort + } + return "" +} + +type RemoveRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RemoveRequest) Reset() { + *x = RemoveRequest{} + mi := &file_redditreader_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemoveRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveRequest) ProtoMessage() {} + +func (x *RemoveRequest) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveRequest.ProtoReflect.Descriptor instead. +func (*RemoveRequest) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{10} +} + +func (x *RemoveRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type FilterMsg struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Subreddit string `protobuf:"bytes,2,opt,name=subreddit,proto3" json:"subreddit,omitempty"` + Pattern string `protobuf:"bytes,3,opt,name=pattern,proto3" json:"pattern,omitempty"` + IsRegex bool `protobuf:"varint,4,opt,name=is_regex,json=isRegex,proto3" json:"is_regex,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FilterMsg) Reset() { + *x = FilterMsg{} + mi := &file_redditreader_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FilterMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FilterMsg) ProtoMessage() {} + +func (x *FilterMsg) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FilterMsg.ProtoReflect.Descriptor instead. +func (*FilterMsg) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{11} +} + +func (x *FilterMsg) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *FilterMsg) GetSubreddit() string { + if x != nil { + return x.Subreddit + } + return "" +} + +func (x *FilterMsg) GetPattern() string { + if x != nil { + return x.Pattern + } + return "" +} + +func (x *FilterMsg) GetIsRegex() bool { + if x != nil { + return x.IsRegex + } + return false +} + +type FilterRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Subreddit string `protobuf:"bytes,1,opt,name=subreddit,proto3" json:"subreddit,omitempty"` + Filters []*FilterMsg `protobuf:"bytes,2,rep,name=filters,proto3" json:"filters,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FilterRequest) Reset() { + *x = FilterRequest{} + mi := &file_redditreader_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FilterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FilterRequest) ProtoMessage() {} + +func (x *FilterRequest) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FilterRequest.ProtoReflect.Descriptor instead. +func (*FilterRequest) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{12} +} + +func (x *FilterRequest) GetSubreddit() string { + if x != nil { + return x.Subreddit + } + return "" +} + +func (x *FilterRequest) GetFilters() []*FilterMsg { + if x != nil { + return x.Filters + } + return nil +} + +type FilterResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Filters []*FilterMsg `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FilterResponse) Reset() { + *x = FilterResponse{} + mi := &file_redditreader_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FilterResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FilterResponse) ProtoMessage() {} + +func (x *FilterResponse) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FilterResponse.ProtoReflect.Descriptor instead. +func (*FilterResponse) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{13} +} + +func (x *FilterResponse) GetFilters() []*FilterMsg { + if x != nil { + return x.Filters + } + return nil +} + +type Empty struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Empty) Reset() { + *x = Empty{} + mi := &file_redditreader_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{14} +} + +type StatusResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + UptimeSeconds int64 `protobuf:"varint,1,opt,name=uptime_seconds,json=uptimeSeconds,proto3" json:"uptime_seconds,omitempty"` + LastPoll string `protobuf:"bytes,2,opt,name=last_poll,json=lastPoll,proto3" json:"last_poll,omitempty"` + TotalPosts int32 `protobuf:"varint,3,opt,name=total_posts,json=totalPosts,proto3" json:"total_posts,omitempty"` + UnreadPosts int32 `protobuf:"varint,4,opt,name=unread_posts,json=unreadPosts,proto3" json:"unread_posts,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StatusResponse) Reset() { + *x = StatusResponse{} + mi := &file_redditreader_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusResponse) ProtoMessage() {} + +func (x *StatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_redditreader_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusResponse.ProtoReflect.Descriptor instead. +func (*StatusResponse) Descriptor() ([]byte, []int) { + return file_redditreader_proto_rawDescGZIP(), []int{15} +} + +func (x *StatusResponse) GetUptimeSeconds() int64 { + if x != nil { + return x.UptimeSeconds + } + return 0 +} + +func (x *StatusResponse) GetLastPoll() string { + if x != nil { + return x.LastPoll + } + return "" +} + +func (x *StatusResponse) GetTotalPosts() int32 { + if x != nil { + return x.TotalPosts + } + return 0 +} + +func (x *StatusResponse) GetUnreadPosts() int32 { + if x != nil { + return x.UnreadPosts + } + return 0 +} + +var File_redditreader_proto protoreflect.FileDescriptor + +const file_redditreader_proto_rawDesc = "" + + "\n" + + "\x12redditreader.proto\x12\fredditreader\x1a\x1fgoogle/protobuf/timestamp.proto\"\xc7\x03\n" + + "\x04Post\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x1c\n" + + "\tsubreddit\x18\x02 \x01(\tR\tsubreddit\x12\x14\n" + + "\x05title\x18\x03 \x01(\tR\x05title\x12\x16\n" + + "\x06author\x18\x04 \x01(\tR\x06author\x12\x10\n" + + "\x03url\x18\x05 \x01(\tR\x03url\x12\x1b\n" + + "\tself_text\x18\x06 \x01(\tR\bselfText\x12\x14\n" + + "\x05score\x18\a \x01(\x05R\x05score\x12;\n" + + "\vcreated_utc\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "createdUtc\x129\n" + + "\n" + + "fetched_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\tfetchedAt\x12!\n" + + "\trelevance\x18\n" + + " \x01(\x01H\x00R\trelevance\x88\x01\x01\x12\x1d\n" + + "\asummary\x18\v \x01(\tH\x01R\asummary\x88\x01\x01\x12\x12\n" + + "\x04read\x18\f \x01(\bR\x04read\x12\x18\n" + + "\astarred\x18\r \x01(\bR\astarred\x12\x1c\n" + + "\tdismissed\x18\x0e \x01(\bR\tdismissedB\f\n" + + "\n" + + "_relevanceB\n" + + "\n" + + "\b_summary\"\x0f\n" + + "\rStreamRequest\"\xc5\x01\n" + + "\vListRequest\x12\x1c\n" + + "\tsubreddit\x18\x01 \x01(\tR\tsubreddit\x12\x1b\n" + + "\x06unread\x18\x02 \x01(\bH\x00R\x06unread\x88\x01\x01\x12\x1d\n" + + "\astarred\x18\x03 \x01(\bH\x01R\astarred\x88\x01\x01\x12!\n" + + "\tdismissed\x18\x04 \x01(\bH\x02R\tdismissed\x88\x01\x01\x12\x14\n" + + "\x05limit\x18\x05 \x01(\x05R\x05limitB\t\n" + + "\a_unreadB\n" + + "\n" + + "\b_starredB\f\n" + + "\n" + + "_dismissed\"8\n" + + "\fListResponse\x12(\n" + + "\x05posts\x18\x01 \x03(\v2\x12.redditreader.PostR\x05posts\"\x9d\x01\n" + + "\rUpdateRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + + "\x04read\x18\x02 \x01(\bH\x00R\x04read\x88\x01\x01\x12\x1d\n" + + "\astarred\x18\x03 \x01(\bH\x01R\astarred\x88\x01\x01\x12!\n" + + "\tdismissed\x18\x04 \x01(\bH\x02R\tdismissed\x88\x01\x01B\a\n" + + "\x05_readB\n" + + "\n" + + "\b_starredB\f\n" + + "\n" + + "_dismissed\">\n" + + "\x0fFeedbackRequest\x12\x17\n" + + "\apost_id\x18\x01 \x01(\tR\x06postId\x12\x12\n" + + "\x04vote\x18\x02 \x01(\x05R\x04vote\"\x12\n" + + "\x10FeedbackResponse\"Y\n" + + "\fSubredditMsg\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" + + "\aenabled\x18\x02 \x01(\bR\aenabled\x12\x1b\n" + + "\tpoll_sort\x18\x03 \x01(\tR\bpollSort\"K\n" + + "\rSubredditList\x12:\n" + + "\n" + + "subreddits\x18\x01 \x03(\v2\x1a.redditreader.SubredditMsgR\n" + + "subreddits\"F\n" + + "\x13AddSubredditRequest\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n" + + "\tpoll_sort\x18\x02 \x01(\tR\bpollSort\"#\n" + + "\rRemoveRequest\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\"n\n" + + "\tFilterMsg\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x03R\x02id\x12\x1c\n" + + "\tsubreddit\x18\x02 \x01(\tR\tsubreddit\x12\x18\n" + + "\apattern\x18\x03 \x01(\tR\apattern\x12\x19\n" + + "\bis_regex\x18\x04 \x01(\bR\aisRegex\"`\n" + + "\rFilterRequest\x12\x1c\n" + + "\tsubreddit\x18\x01 \x01(\tR\tsubreddit\x121\n" + + "\afilters\x18\x02 \x03(\v2\x17.redditreader.FilterMsgR\afilters\"C\n" + + "\x0eFilterResponse\x121\n" + + "\afilters\x18\x01 \x03(\v2\x17.redditreader.FilterMsgR\afilters\"\a\n" + + "\x05Empty\"\x98\x01\n" + + "\x0eStatusResponse\x12%\n" + + "\x0euptime_seconds\x18\x01 \x01(\x03R\ruptimeSeconds\x12\x1b\n" + + "\tlast_poll\x18\x02 \x01(\tR\blastPoll\x12\x1f\n" + + "\vtotal_posts\x18\x03 \x01(\x05R\n" + + "totalPosts\x12!\n" + + "\funread_posts\x18\x04 \x01(\x05R\vunreadPosts2\x85\x05\n" + + "\fRedditReader\x12@\n" + + "\vStreamPosts\x12\x1b.redditreader.StreamRequest\x1a\x12.redditreader.Post0\x01\x12B\n" + + "\tListPosts\x12\x19.redditreader.ListRequest\x1a\x1a.redditreader.ListResponse\x12=\n" + + "\n" + + "UpdatePost\x12\x1b.redditreader.UpdateRequest\x1a\x12.redditreader.Post\x12O\n" + + "\x0eSubmitFeedback\x12\x1d.redditreader.FeedbackRequest\x1a\x1e.redditreader.FeedbackResponse\x12B\n" + + "\x0eListSubreddits\x12\x13.redditreader.Empty\x1a\x1b.redditreader.SubredditList\x12M\n" + + "\fAddSubreddit\x12!.redditreader.AddSubredditRequest\x1a\x1a.redditreader.SubredditMsg\x12C\n" + + "\x0fRemoveSubreddit\x12\x1b.redditreader.RemoveRequest\x1a\x13.redditreader.Empty\x12J\n" + + "\rUpdateFilters\x12\x1b.redditreader.FilterRequest\x1a\x1c.redditreader.FilterResponse\x12;\n" + + "\x06Status\x12\x13.redditreader.Empty\x1a\x1c.redditreader.StatusResponseB8Z6somegit.dev/vikingowl/reddit-reader/proto/redditreaderb\x06proto3" + +var ( + file_redditreader_proto_rawDescOnce sync.Once + file_redditreader_proto_rawDescData []byte +) + +func file_redditreader_proto_rawDescGZIP() []byte { + file_redditreader_proto_rawDescOnce.Do(func() { + file_redditreader_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_redditreader_proto_rawDesc), len(file_redditreader_proto_rawDesc))) + }) + return file_redditreader_proto_rawDescData +} + +var file_redditreader_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_redditreader_proto_goTypes = []any{ + (*Post)(nil), // 0: redditreader.Post + (*StreamRequest)(nil), // 1: redditreader.StreamRequest + (*ListRequest)(nil), // 2: redditreader.ListRequest + (*ListResponse)(nil), // 3: redditreader.ListResponse + (*UpdateRequest)(nil), // 4: redditreader.UpdateRequest + (*FeedbackRequest)(nil), // 5: redditreader.FeedbackRequest + (*FeedbackResponse)(nil), // 6: redditreader.FeedbackResponse + (*SubredditMsg)(nil), // 7: redditreader.SubredditMsg + (*SubredditList)(nil), // 8: redditreader.SubredditList + (*AddSubredditRequest)(nil), // 9: redditreader.AddSubredditRequest + (*RemoveRequest)(nil), // 10: redditreader.RemoveRequest + (*FilterMsg)(nil), // 11: redditreader.FilterMsg + (*FilterRequest)(nil), // 12: redditreader.FilterRequest + (*FilterResponse)(nil), // 13: redditreader.FilterResponse + (*Empty)(nil), // 14: redditreader.Empty + (*StatusResponse)(nil), // 15: redditreader.StatusResponse + (*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp +} +var file_redditreader_proto_depIdxs = []int32{ + 16, // 0: redditreader.Post.created_utc:type_name -> google.protobuf.Timestamp + 16, // 1: redditreader.Post.fetched_at:type_name -> google.protobuf.Timestamp + 0, // 2: redditreader.ListResponse.posts:type_name -> redditreader.Post + 7, // 3: redditreader.SubredditList.subreddits:type_name -> redditreader.SubredditMsg + 11, // 4: redditreader.FilterRequest.filters:type_name -> redditreader.FilterMsg + 11, // 5: redditreader.FilterResponse.filters:type_name -> redditreader.FilterMsg + 1, // 6: redditreader.RedditReader.StreamPosts:input_type -> redditreader.StreamRequest + 2, // 7: redditreader.RedditReader.ListPosts:input_type -> redditreader.ListRequest + 4, // 8: redditreader.RedditReader.UpdatePost:input_type -> redditreader.UpdateRequest + 5, // 9: redditreader.RedditReader.SubmitFeedback:input_type -> redditreader.FeedbackRequest + 14, // 10: redditreader.RedditReader.ListSubreddits:input_type -> redditreader.Empty + 9, // 11: redditreader.RedditReader.AddSubreddit:input_type -> redditreader.AddSubredditRequest + 10, // 12: redditreader.RedditReader.RemoveSubreddit:input_type -> redditreader.RemoveRequest + 12, // 13: redditreader.RedditReader.UpdateFilters:input_type -> redditreader.FilterRequest + 14, // 14: redditreader.RedditReader.Status:input_type -> redditreader.Empty + 0, // 15: redditreader.RedditReader.StreamPosts:output_type -> redditreader.Post + 3, // 16: redditreader.RedditReader.ListPosts:output_type -> redditreader.ListResponse + 0, // 17: redditreader.RedditReader.UpdatePost:output_type -> redditreader.Post + 6, // 18: redditreader.RedditReader.SubmitFeedback:output_type -> redditreader.FeedbackResponse + 8, // 19: redditreader.RedditReader.ListSubreddits:output_type -> redditreader.SubredditList + 7, // 20: redditreader.RedditReader.AddSubreddit:output_type -> redditreader.SubredditMsg + 14, // 21: redditreader.RedditReader.RemoveSubreddit:output_type -> redditreader.Empty + 13, // 22: redditreader.RedditReader.UpdateFilters:output_type -> redditreader.FilterResponse + 15, // 23: redditreader.RedditReader.Status:output_type -> redditreader.StatusResponse + 15, // [15:24] is the sub-list for method output_type + 6, // [6:15] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_redditreader_proto_init() } +func file_redditreader_proto_init() { + if File_redditreader_proto != nil { + return + } + file_redditreader_proto_msgTypes[0].OneofWrappers = []any{} + file_redditreader_proto_msgTypes[2].OneofWrappers = []any{} + file_redditreader_proto_msgTypes[4].OneofWrappers = []any{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_redditreader_proto_rawDesc), len(file_redditreader_proto_rawDesc)), + NumEnums: 0, + NumMessages: 16, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_redditreader_proto_goTypes, + DependencyIndexes: file_redditreader_proto_depIdxs, + MessageInfos: file_redditreader_proto_msgTypes, + }.Build() + File_redditreader_proto = out.File + file_redditreader_proto_goTypes = nil + file_redditreader_proto_depIdxs = nil +} diff --git a/proto/redditreader/redditreader.proto b/proto/redditreader/redditreader.proto new file mode 100644 index 0000000..b39d33f --- /dev/null +++ b/proto/redditreader/redditreader.proto @@ -0,0 +1,108 @@ +syntax = "proto3"; + +package redditreader; + +option go_package = "somegit.dev/vikingowl/reddit-reader/proto/redditreader"; + +import "google/protobuf/timestamp.proto"; + +message Post { + string id = 1; + string subreddit = 2; + string title = 3; + string author = 4; + string url = 5; + string self_text = 6; + int32 score = 7; + google.protobuf.Timestamp created_utc = 8; + google.protobuf.Timestamp fetched_at = 9; + optional double relevance = 10; + optional string summary = 11; + bool read = 12; + bool starred = 13; + bool dismissed = 14; +} + +message StreamRequest {} + +message ListRequest { + string subreddit = 1; + optional bool unread = 2; + optional bool starred = 3; + optional bool dismissed = 4; + int32 limit = 5; +} + +message ListResponse { + repeated Post posts = 1; +} + +message UpdateRequest { + string id = 1; + optional bool read = 2; + optional bool starred = 3; + optional bool dismissed = 4; +} + +message FeedbackRequest { + string post_id = 1; + int32 vote = 2; +} + +message FeedbackResponse {} + +message SubredditMsg { + string name = 1; + bool enabled = 2; + string poll_sort = 3; +} + +message SubredditList { + repeated SubredditMsg subreddits = 1; +} + +message AddSubredditRequest { + string name = 1; + string poll_sort = 2; +} + +message RemoveRequest { + string name = 1; +} + +message FilterMsg { + int64 id = 1; + string subreddit = 2; + string pattern = 3; + bool is_regex = 4; +} + +message FilterRequest { + string subreddit = 1; + repeated FilterMsg filters = 2; +} + +message FilterResponse { + repeated FilterMsg filters = 1; +} + +message Empty {} + +message StatusResponse { + int64 uptime_seconds = 1; + string last_poll = 2; + int32 total_posts = 3; + int32 unread_posts = 4; +} + +service RedditReader { + rpc StreamPosts(StreamRequest) returns (stream Post); + rpc ListPosts(ListRequest) returns (ListResponse); + rpc UpdatePost(UpdateRequest) returns (Post); + rpc SubmitFeedback(FeedbackRequest) returns (FeedbackResponse); + rpc ListSubreddits(Empty) returns (SubredditList); + rpc AddSubreddit(AddSubredditRequest) returns (SubredditMsg); + rpc RemoveSubreddit(RemoveRequest) returns (Empty); + rpc UpdateFilters(FilterRequest) returns (FilterResponse); + rpc Status(Empty) returns (StatusResponse); +} diff --git a/proto/redditreader/redditreader_grpc.pb.go b/proto/redditreader/redditreader_grpc.pb.go new file mode 100644 index 0000000..5b2cc50 --- /dev/null +++ b/proto/redditreader/redditreader_grpc.pb.go @@ -0,0 +1,429 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.0 +// - protoc v6.33.1 +// source: redditreader.proto + +package redditreader + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + RedditReader_StreamPosts_FullMethodName = "/redditreader.RedditReader/StreamPosts" + RedditReader_ListPosts_FullMethodName = "/redditreader.RedditReader/ListPosts" + RedditReader_UpdatePost_FullMethodName = "/redditreader.RedditReader/UpdatePost" + RedditReader_SubmitFeedback_FullMethodName = "/redditreader.RedditReader/SubmitFeedback" + RedditReader_ListSubreddits_FullMethodName = "/redditreader.RedditReader/ListSubreddits" + RedditReader_AddSubreddit_FullMethodName = "/redditreader.RedditReader/AddSubreddit" + RedditReader_RemoveSubreddit_FullMethodName = "/redditreader.RedditReader/RemoveSubreddit" + RedditReader_UpdateFilters_FullMethodName = "/redditreader.RedditReader/UpdateFilters" + RedditReader_Status_FullMethodName = "/redditreader.RedditReader/Status" +) + +// RedditReaderClient is the client API for RedditReader service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type RedditReaderClient interface { + StreamPosts(ctx context.Context, in *StreamRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Post], error) + ListPosts(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) + UpdatePost(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Post, error) + SubmitFeedback(ctx context.Context, in *FeedbackRequest, opts ...grpc.CallOption) (*FeedbackResponse, error) + ListSubreddits(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*SubredditList, error) + AddSubreddit(ctx context.Context, in *AddSubredditRequest, opts ...grpc.CallOption) (*SubredditMsg, error) + RemoveSubreddit(ctx context.Context, in *RemoveRequest, opts ...grpc.CallOption) (*Empty, error) + UpdateFilters(ctx context.Context, in *FilterRequest, opts ...grpc.CallOption) (*FilterResponse, error) + Status(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StatusResponse, error) +} + +type redditReaderClient struct { + cc grpc.ClientConnInterface +} + +func NewRedditReaderClient(cc grpc.ClientConnInterface) RedditReaderClient { + return &redditReaderClient{cc} +} + +func (c *redditReaderClient) StreamPosts(ctx context.Context, in *StreamRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Post], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &RedditReader_ServiceDesc.Streams[0], RedditReader_StreamPosts_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[StreamRequest, Post]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type RedditReader_StreamPostsClient = grpc.ServerStreamingClient[Post] + +func (c *redditReaderClient) ListPosts(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListResponse) + err := c.cc.Invoke(ctx, RedditReader_ListPosts_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *redditReaderClient) UpdatePost(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Post, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Post) + err := c.cc.Invoke(ctx, RedditReader_UpdatePost_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *redditReaderClient) SubmitFeedback(ctx context.Context, in *FeedbackRequest, opts ...grpc.CallOption) (*FeedbackResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(FeedbackResponse) + err := c.cc.Invoke(ctx, RedditReader_SubmitFeedback_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *redditReaderClient) ListSubreddits(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*SubredditList, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SubredditList) + err := c.cc.Invoke(ctx, RedditReader_ListSubreddits_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *redditReaderClient) AddSubreddit(ctx context.Context, in *AddSubredditRequest, opts ...grpc.CallOption) (*SubredditMsg, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SubredditMsg) + err := c.cc.Invoke(ctx, RedditReader_AddSubreddit_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *redditReaderClient) RemoveSubreddit(ctx context.Context, in *RemoveRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, RedditReader_RemoveSubreddit_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *redditReaderClient) UpdateFilters(ctx context.Context, in *FilterRequest, opts ...grpc.CallOption) (*FilterResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(FilterResponse) + err := c.cc.Invoke(ctx, RedditReader_UpdateFilters_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *redditReaderClient) Status(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StatusResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(StatusResponse) + err := c.cc.Invoke(ctx, RedditReader_Status_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// RedditReaderServer is the server API for RedditReader service. +// All implementations must embed UnimplementedRedditReaderServer +// for forward compatibility. +type RedditReaderServer interface { + StreamPosts(*StreamRequest, grpc.ServerStreamingServer[Post]) error + ListPosts(context.Context, *ListRequest) (*ListResponse, error) + UpdatePost(context.Context, *UpdateRequest) (*Post, error) + SubmitFeedback(context.Context, *FeedbackRequest) (*FeedbackResponse, error) + ListSubreddits(context.Context, *Empty) (*SubredditList, error) + AddSubreddit(context.Context, *AddSubredditRequest) (*SubredditMsg, error) + RemoveSubreddit(context.Context, *RemoveRequest) (*Empty, error) + UpdateFilters(context.Context, *FilterRequest) (*FilterResponse, error) + Status(context.Context, *Empty) (*StatusResponse, error) + mustEmbedUnimplementedRedditReaderServer() +} + +// UnimplementedRedditReaderServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedRedditReaderServer struct{} + +func (UnimplementedRedditReaderServer) StreamPosts(*StreamRequest, grpc.ServerStreamingServer[Post]) error { + return status.Error(codes.Unimplemented, "method StreamPosts not implemented") +} +func (UnimplementedRedditReaderServer) ListPosts(context.Context, *ListRequest) (*ListResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListPosts not implemented") +} +func (UnimplementedRedditReaderServer) UpdatePost(context.Context, *UpdateRequest) (*Post, error) { + return nil, status.Error(codes.Unimplemented, "method UpdatePost not implemented") +} +func (UnimplementedRedditReaderServer) SubmitFeedback(context.Context, *FeedbackRequest) (*FeedbackResponse, error) { + return nil, status.Error(codes.Unimplemented, "method SubmitFeedback not implemented") +} +func (UnimplementedRedditReaderServer) ListSubreddits(context.Context, *Empty) (*SubredditList, error) { + return nil, status.Error(codes.Unimplemented, "method ListSubreddits not implemented") +} +func (UnimplementedRedditReaderServer) AddSubreddit(context.Context, *AddSubredditRequest) (*SubredditMsg, error) { + return nil, status.Error(codes.Unimplemented, "method AddSubreddit not implemented") +} +func (UnimplementedRedditReaderServer) RemoveSubreddit(context.Context, *RemoveRequest) (*Empty, error) { + return nil, status.Error(codes.Unimplemented, "method RemoveSubreddit not implemented") +} +func (UnimplementedRedditReaderServer) UpdateFilters(context.Context, *FilterRequest) (*FilterResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateFilters not implemented") +} +func (UnimplementedRedditReaderServer) Status(context.Context, *Empty) (*StatusResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Status not implemented") +} +func (UnimplementedRedditReaderServer) mustEmbedUnimplementedRedditReaderServer() {} +func (UnimplementedRedditReaderServer) testEmbeddedByValue() {} + +// UnsafeRedditReaderServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to RedditReaderServer will +// result in compilation errors. +type UnsafeRedditReaderServer interface { + mustEmbedUnimplementedRedditReaderServer() +} + +func RegisterRedditReaderServer(s grpc.ServiceRegistrar, srv RedditReaderServer) { + // If the following call panics, it indicates UnimplementedRedditReaderServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&RedditReader_ServiceDesc, srv) +} + +func _RedditReader_StreamPosts_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(StreamRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(RedditReaderServer).StreamPosts(m, &grpc.GenericServerStream[StreamRequest, Post]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type RedditReader_StreamPostsServer = grpc.ServerStreamingServer[Post] + +func _RedditReader_ListPosts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RedditReaderServer).ListPosts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RedditReader_ListPosts_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RedditReaderServer).ListPosts(ctx, req.(*ListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RedditReader_UpdatePost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RedditReaderServer).UpdatePost(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RedditReader_UpdatePost_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RedditReaderServer).UpdatePost(ctx, req.(*UpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RedditReader_SubmitFeedback_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FeedbackRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RedditReaderServer).SubmitFeedback(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RedditReader_SubmitFeedback_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RedditReaderServer).SubmitFeedback(ctx, req.(*FeedbackRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RedditReader_ListSubreddits_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RedditReaderServer).ListSubreddits(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RedditReader_ListSubreddits_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RedditReaderServer).ListSubreddits(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _RedditReader_AddSubreddit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddSubredditRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RedditReaderServer).AddSubreddit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RedditReader_AddSubreddit_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RedditReaderServer).AddSubreddit(ctx, req.(*AddSubredditRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RedditReader_RemoveSubreddit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RedditReaderServer).RemoveSubreddit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RedditReader_RemoveSubreddit_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RedditReaderServer).RemoveSubreddit(ctx, req.(*RemoveRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RedditReader_UpdateFilters_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FilterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RedditReaderServer).UpdateFilters(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RedditReader_UpdateFilters_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RedditReaderServer).UpdateFilters(ctx, req.(*FilterRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RedditReader_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RedditReaderServer).Status(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RedditReader_Status_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RedditReaderServer).Status(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +// RedditReader_ServiceDesc is the grpc.ServiceDesc for RedditReader service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var RedditReader_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "redditreader.RedditReader", + HandlerType: (*RedditReaderServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListPosts", + Handler: _RedditReader_ListPosts_Handler, + }, + { + MethodName: "UpdatePost", + Handler: _RedditReader_UpdatePost_Handler, + }, + { + MethodName: "SubmitFeedback", + Handler: _RedditReader_SubmitFeedback_Handler, + }, + { + MethodName: "ListSubreddits", + Handler: _RedditReader_ListSubreddits_Handler, + }, + { + MethodName: "AddSubreddit", + Handler: _RedditReader_AddSubreddit_Handler, + }, + { + MethodName: "RemoveSubreddit", + Handler: _RedditReader_RemoveSubreddit_Handler, + }, + { + MethodName: "UpdateFilters", + Handler: _RedditReader_UpdateFilters_Handler, + }, + { + MethodName: "Status", + Handler: _RedditReader_Status_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "StreamPosts", + Handler: _RedditReader_StreamPosts_Handler, + ServerStreams: true, + }, + }, + Metadata: "redditreader.proto", +}