|
| 1 | +// Exit codes: |
| 2 | +// 0 - Success or help displayed |
| 3 | +// 1 - Error (invalid args, API failure, etc.) |
1 | 4 | package main |
2 | 5 |
|
3 | 6 | import ( |
4 | 7 | "encoding/json" |
5 | 8 | "flag" |
6 | 9 | "fmt" |
7 | | - "github.com/ByteSizedMarius/rewerse-engineering/pkg" |
8 | 10 | "os" |
9 | | - "strings" |
10 | | -) |
11 | 11 |
|
12 | | -func hdl(err error) { |
13 | | - if err != nil { |
14 | | - fmt.Println(err) |
15 | | - os.Exit(1) |
16 | | - } |
17 | | -} |
| 12 | + rewerse "github.com/ByteSizedMarius/rewerse-engineering/pkg" |
| 13 | +) |
18 | 14 |
|
19 | 15 | func main() { |
20 | | - flag.Usage = func() { |
21 | | - fmt.Println("Usage: rewerse-cli [flags] [subcommand] [subcommand-flags]") |
22 | | - fmt.Println("\nFlags:") |
23 | | - fmt.Println(align("-cert <path>") + "Path to the certificate file (default 'certificate.pem')") |
24 | | - fmt.Println(align("-key <path>") + "Path to the key file (default 'private.key')") |
25 | | - fmt.Println(align("-json") + "Output in JSON format (default false)") |
26 | | - fmt.Println("\nSubcommands:") |
27 | | - fmt.Println(align("marketsearch -query <query>") + "Search for markets") |
28 | | - fmt.Println(align("marketdetails -id <market-id>") + "Get details for a market") |
29 | | - fmt.Println(align("coupons") + "Get coupons") |
30 | | - fmt.Println(align("recalls") + "Get recalls") |
31 | | - fmt.Println(align("discounts -id <market-id> [-raw] [-catGroup]") + "Get discounts") |
32 | | - fmt.Println(align("categories -id <market-id> [-printAll]") + "Get product categories") |
33 | | - fmt.Println(align("products -id <market-id> [-category <category> -search <query>] [-page <page>] [-perPage <productsPerPage>]") + "\n" + align("") + "Get products from a category or by query") |
34 | | - fmt.Println("\nSubcommand-Flags:") |
35 | | - fmt.Println(align("-query <query>") + "Search query. Can be a city, postal code, street, etc.") |
36 | | - fmt.Println(align("-id <market-id>") + "ID of the market or discount. Get it from marketsearch.") |
37 | | - fmt.Println(align("-raw") + "Whether you want raw output format (directly from the API) or parsed.") |
38 | | - fmt.Println(align("-catGroup") + "Group by product category instead of rewe-app category") |
39 | | - fmt.Println(align("-printAll") + "Print all available product categories (very many)") |
40 | | - fmt.Println(align("-category <category>") + "The slug of the category to fetch the products from") |
41 | | - fmt.Println(align("-search <query>") + "Search query for products. Can be any term or EANs for example") |
42 | | - fmt.Println(align("-page <page>") + "Page number for pagination. Starts at 1, default 1. The amount of available pages is included in the output") |
43 | | - fmt.Println(align("-perPage <productsPerPage>") + "Number of products per page. Default 30") |
44 | | - fmt.Println("\nExamples:") |
45 | | - fmt.Println(" rewerse.exe -cert cert.pem -key p.key marketsearch -query Köln") |
46 | | - fmt.Println(" rewerse.exe marketsearch -query Köln") |
47 | | - fmt.Println(" rewerse.exe marketdetails -id 1763153") |
48 | | - fmt.Println(" rewerse.exe discounts -id 1763153") |
49 | | - fmt.Println(" rewerse.exe -json discounts -id 1763153 -raw") |
50 | | - fmt.Println(" rewerse.exe discounts -id 1763153 -catGroup") |
51 | | - fmt.Println(" rewerse.exe categories -id 831002 -printAll") |
52 | | - fmt.Println(" rewerse.exe products -id 831002 -category kueche-haushalt -page 2 -perPage 10") |
53 | | - fmt.Println(" rewerse.exe products -id 831002 -search Karotten") |
54 | | - } |
55 | | - |
| 16 | + // Global flags |
56 | 17 | certFile := flag.String("cert", "", "Path to the certificate file") |
57 | 18 | keyFile := flag.String("key", "", "Path to the key file") |
58 | | - jsonOutput := flag.Bool("json", false, "Output in JSON format (default false)") |
| 19 | + jsonOutput := flag.Bool("json", false, "Output in JSON format") |
| 20 | + flag.Usage = mainHelp |
59 | 21 | flag.Parse() |
60 | 22 |
|
61 | 23 | if flag.NArg() == 0 { |
62 | | - flag.Usage() |
63 | | - os.Exit(1) |
| 24 | + mainHelp() |
| 25 | + os.Exit(0) |
64 | 26 | } |
65 | 27 |
|
66 | | - var crt, key string |
67 | | - if *certFile == "" { |
68 | | - crt = "certificate.pem" |
69 | | - } else { |
| 28 | + // Load certificate |
| 29 | + crt := "certificate.pem" |
| 30 | + key := "private.key" |
| 31 | + if *certFile != "" { |
70 | 32 | crt = *certFile |
71 | 33 | } |
72 | | - if *keyFile == "" { |
73 | | - key = "private.key" |
74 | | - } else { |
| 34 | + if *keyFile != "" { |
75 | 35 | key = *keyFile |
76 | 36 | } |
77 | 37 |
|
78 | | - var err error |
79 | | - err = rewerse.SetCertificate(crt, key) |
80 | | - if err != nil && *certFile == "" && *keyFile == "" { |
81 | | - fmt.Println("Please provide the paths to the certificate and key files.\n\nrewerse.exe -cert cert.pem -key p.key [...]") |
| 38 | + if err := rewerse.SetCertificate(crt, key); err != nil { |
| 39 | + if *certFile == "" && *keyFile == "" { |
| 40 | + fmt.Fprintln(os.Stderr, "Certificate not found. Use -cert and -key flags or place certificate.pem and private.key in current directory.") |
| 41 | + } else { |
| 42 | + fmt.Fprintf(os.Stderr, "Error loading certificate: %v\n", err) |
| 43 | + } |
82 | 44 | os.Exit(1) |
83 | 45 | } |
84 | | - hdl(err) |
85 | | - |
86 | | - marketSearchCmd := flag.NewFlagSet("marketsearch", flag.ExitOnError) |
87 | | - marketSearchQuery := marketSearchCmd.String("query", "", "Search query") |
88 | | - |
89 | | - marketDetailsCmd := flag.NewFlagSet("marketdetails", flag.ExitOnError) |
90 | | - marketDetailsID := marketDetailsCmd.String("id", "", "ID of the market details") |
91 | | - |
92 | | - discountsCmd := flag.NewFlagSet("discounts", flag.ExitOnError) |
93 | | - discountsID := discountsCmd.String("id", "", "Market-ID") |
94 | | - rawOutput := discountsCmd.Bool("raw", false, "Whether to return raw API Data") |
95 | | - groupProduct := discountsCmd.Bool("catGroup", false, "Group by product category instead of app-category") |
96 | | - |
97 | | - pcategories := flag.NewFlagSet("categories", flag.ExitOnError) |
98 | | - pcategoriesMarketID := pcategories.String("id", "", "Market-ID") |
99 | | - all := pcategories.Bool("printAll", false, "Print all available product categories") |
100 | | - |
101 | | - products := flag.NewFlagSet("products", flag.ExitOnError) |
102 | | - productsMarketID := products.String("id", "", "Market-ID") |
103 | | - productsCategory := products.String("category", "", "Category slug") |
104 | | - productsSearch := products.String("search", "", "Search query") |
105 | | - productsPage := products.Int("page", 0, "Page number") |
106 | | - productsPerPage := products.Int("perPage", 0, "Products per page") |
107 | 46 |
|
108 | 47 | var data any |
| 48 | + var err error |
| 49 | + |
109 | 50 | switch flag.Arg(0) { |
110 | | - case "marketsearch": |
111 | | - hdl(marketSearchCmd.Parse(flag.Args()[1:])) |
112 | | - if *marketSearchQuery == "" { |
113 | | - fmt.Println("Expected search query") |
114 | | - os.Exit(1) |
115 | | - } |
116 | | - data, err = rewerse.MarketSearch(*marketSearchQuery) |
117 | | - case "marketdetails": |
118 | | - hdl(marketDetailsCmd.Parse(flag.Args()[1:])) |
119 | | - if *marketDetailsID == "" { |
120 | | - fmt.Println("Expected market ID") |
121 | | - os.Exit(1) |
| 51 | + case "markets": |
| 52 | + data, err = handleMarkets(flag.Args()[1:]) |
| 53 | + case "products": |
| 54 | + data, err = handleProducts(flag.Args()[1:]) |
| 55 | + case "recipes": |
| 56 | + data, err = handleRecipes(flag.Args()[1:], *jsonOutput) |
| 57 | + if data == nil && err == nil { |
| 58 | + return // already printed |
122 | 59 | } |
123 | | - data, err = rewerse.GetMarketDetails(*marketDetailsID) |
124 | | - case "coupons": |
125 | | - var oc rewerse.OneScanCoupons |
126 | | - var c rewerse.Coupons |
127 | | - oc, c, err = rewerse.GetCoupons() |
128 | | - data = []any{oc, c} |
129 | | - case "recalls": |
130 | | - data, err = rewerse.GetRecalls() |
131 | 60 | case "discounts": |
132 | | - hdl(discountsCmd.Parse(flag.Args()[1:])) |
133 | | - if *rawOutput { |
134 | | - data, err = rewerse.GetDiscountsRaw(*discountsID) |
135 | | - *jsonOutput = true |
136 | | - } else { |
137 | | - data, err = rewerse.GetDiscounts(*discountsID) |
138 | | - } |
139 | | - if *groupProduct { |
140 | | - data = data.(rewerse.Discounts).GroupByProductCategory() |
141 | | - } |
| 61 | + data, err = handleDiscounts(flag.Args()[1:]) |
142 | 62 | case "categories": |
143 | | - hdl(pcategories.Parse(flag.Args()[1:])) |
144 | | - if *pcategoriesMarketID == "" { |
145 | | - fmt.Println("Expected market ID") |
146 | | - os.Exit(1) |
147 | | - } |
148 | | - |
149 | | - var so rewerse.ShopOverview |
150 | | - so, err = rewerse.GetShopOverview(*pcategoriesMarketID) |
151 | | - hdl(err) |
152 | | - |
153 | | - if *all && *jsonOutput { |
154 | | - fmt.Println("Cannot print all and output in JSON format (json is all by default)") |
155 | | - return |
156 | | - } |
157 | | - |
158 | | - if *all { |
159 | | - fmt.Println(so.StringAll()) |
160 | | - return |
161 | | - } else if !*jsonOutput { |
162 | | - fmt.Println(so.String()) |
163 | | - return |
164 | | - } else { |
165 | | - data = so.ProductCategories |
166 | | - } |
167 | | - case "products": |
168 | | - hdl(products.Parse(flag.Args()[1:])) |
169 | | - if *productsMarketID == "" { |
170 | | - fmt.Println("Expected market ID") |
171 | | - os.Exit(1) |
172 | | - } |
173 | | - |
174 | | - if *productsCategory == "" && *productsSearch == "" { |
175 | | - fmt.Println("Expected category or search query") |
176 | | - os.Exit(1) |
177 | | - } |
178 | | - |
179 | | - if *productsCategory != "" && *productsSearch != "" { |
180 | | - fmt.Println("Expected either category or search query, not both") |
181 | | - os.Exit(1) |
182 | | - } |
183 | | - |
184 | | - var opts *rewerse.ProductOpts |
185 | | - if *productsPage != 0 { |
186 | | - opts = &rewerse.ProductOpts{ |
187 | | - Page: *productsPage, |
188 | | - } |
| 63 | + data, err = handleCategories(flag.Args()[1:], *jsonOutput) |
| 64 | + if data == nil { |
| 65 | + return // already printed |
189 | 66 | } |
190 | | - |
191 | | - if *productsPerPage != 0 { |
192 | | - if opts == nil { |
193 | | - opts = &rewerse.ProductOpts{ |
194 | | - ObjectsPerPage: *productsPerPage, |
195 | | - } |
196 | | - } else { |
197 | | - opts.ObjectsPerPage = *productsPerPage |
198 | | - } |
199 | | - } |
200 | | - |
201 | | - if *productsCategory != "" { |
202 | | - data, err = rewerse.GetCategoryProducts(*productsMarketID, *productsCategory, opts) |
203 | | - } else { |
204 | | - data, err = rewerse.GetProducts(*productsMarketID, *productsSearch, opts) |
205 | | - } |
206 | | - |
| 67 | + case "recalls": |
| 68 | + data, err = rewerse.GetRecalls() |
| 69 | + case "services": |
| 70 | + data, err = handleServices(flag.Args()[1:]) |
207 | 71 | default: |
208 | | - fmt.Println("Expected one of the following subcommands:\n- marketsearch\n- marketdetails\n- coupons\n- recalls\n- discounts\n- categories\n- products") |
| 72 | + fmt.Fprintf(os.Stderr, "Unknown command: %s\n\n", flag.Arg(0)) |
| 73 | + mainHelp() |
209 | 74 | os.Exit(1) |
210 | 75 | } |
211 | 76 |
|
212 | | - hdl(err) |
213 | | - if *jsonOutput { |
214 | | - var bt []byte |
215 | | - bt, err = json.MarshalIndent(data, "", " ") |
216 | | - hdl(err) |
217 | | - data = string(bt) |
| 77 | + if err != nil { |
| 78 | + fmt.Fprintf(os.Stderr, "Error: %v\n", err) |
| 79 | + os.Exit(1) |
218 | 80 | } |
219 | | - fmt.Println(data) |
220 | | -} |
221 | 81 |
|
222 | | -func align(s string) string { |
223 | | - al := 50 |
224 | | - if len(s) > al { |
225 | | - al = 0 |
| 82 | + if *jsonOutput { |
| 83 | + bt, err := json.MarshalIndent(data, "", " ") |
| 84 | + if err != nil { |
| 85 | + fmt.Fprintf(os.Stderr, "Error: %v\n", err) |
| 86 | + os.Exit(1) |
| 87 | + } |
| 88 | + fmt.Println(string(bt)) |
226 | 89 | } else { |
227 | | - al -= len(s) |
| 90 | + fmt.Println(data) |
228 | 91 | } |
| 92 | +} |
229 | 93 |
|
230 | | - return " " + s + strings.Repeat(" ", al) |
| 94 | +func mainHelp() { |
| 95 | + fmt.Printf(`Usage: %s [flags] <command> [subcommand] [flags] |
| 96 | +
|
| 97 | +Flags: |
| 98 | + -cert <path> Certificate file (default: certificate.pem) |
| 99 | + -key <path> Key file (default: private.key) |
| 100 | + -json Output as JSON |
| 101 | +
|
| 102 | +Commands: |
| 103 | + markets Search and get market details |
| 104 | + products Search, browse, and get product info |
| 105 | + recipes Search and browse recipes |
| 106 | + discounts Get market discounts |
| 107 | + categories Get product categories |
| 108 | + recalls Get product recalls |
| 109 | + services Get service portfolio by zip |
| 110 | +
|
| 111 | +Examples: |
| 112 | + %s markets search -query Köln |
| 113 | + %s products search -market 831002 -query Milch |
| 114 | + %s products category -market 831002 -slug obst-gemuese |
| 115 | + %s recipes search -term Pasta |
| 116 | + %s discounts -market 840174 |
| 117 | + %s categories -market 831002 |
| 118 | + %s services -zip 50667 |
| 119 | +
|
| 120 | +Run '%s <command>' for subcommand help. |
| 121 | +`, binaryName, binaryName, binaryName, binaryName, binaryName, binaryName, binaryName, binaryName, binaryName) |
231 | 122 | } |
0 commit comments