diff options
| author | Pridon Tetradze <[email protected]> | 2022-06-26 13:46:59 +0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-06-26 13:46:59 +0400 |
| commit | 2e058526d896ab4ee5173ceb80d1248ac08ff594 (patch) | |
| tree | 9550b812ea66f069396fef043cff1d10aeaec859 | |
| parent | 8626e2d8e258e11837d27a1e99ef3277e7dfdf3b (diff) | |
| download | countryfetch-2e058526d896ab4ee5173ceb80d1248ac08ff594.tar.xz countryfetch-2e058526d896ab4ee5173ceb80d1248ac08ff594.zip | |
Flag ASCII art, remove tabularization & refactor (#3)
* initial flag image converter
* add flags caching
* refactor, remove tabularization, make flag ASCII optional.
* update readme
* update readme
| -rw-r--r-- | README.md | 44 | ||||
| -rw-r--r-- | images/countryfetch.png | bin | 1595036 -> 227638 bytes | |||
| -rw-r--r-- | index.ts | 3 | ||||
| -rwxr-xr-x | install.sh | 2 | ||||
| -rw-r--r-- | main.ts | 12 | ||||
| -rwxr-xr-x | run.sh | 2 | ||||
| -rw-r--r-- | src/app.ts | 9 | ||||
| -rw-r--r-- | src/countries.ts | 51 | ||||
| -rw-r--r-- | src/environment/environment.ts | 8 | ||||
| -rw-r--r-- | src/main.ts | 15 | ||||
| -rw-r--r-- | src/models/country.model.ts | 3 | ||||
| -rw-r--r-- | src/models/fetched-country.model.ts (renamed from src/models/FetchedCountry.model.ts) | 0 | ||||
| -rw-r--r-- | src/models/flag-ascii.model.ts | 4 | ||||
| -rw-r--r-- | src/util/image-converter.ts | 22 | ||||
| -rw-r--r-- | src/util/logger.ts | 30 |
15 files changed, 155 insertions, 50 deletions
@@ -1,6 +1,6 @@ # countryfetch -A cli tool for fetching information about countries. It uses https://restcountries.com/ API for backend. +**A cli tool for fetching information about countries. It uses https://restcountries.com/ API for backend.**  @@ -49,6 +49,8 @@ countryfetch <arguments> - `<country_name>` - Find country information by name. - `capital <capital>` - Find country to which the specified capital belongs. - `sync` - Fetches data from API and stores it in `~/.cache/countryfetch/countries.json`. This is done automatically, but can be triggered manually. + Pass additional argument `sync flag` to fetch and convert flags in ASCII art. + After syncing flags, every countryfetch command will display flag ASCII art. - `random` - Get random country information. - `raw` - Print country information in raw format as JavaScript object. @@ -57,18 +59,33 @@ countryfetch <arguments> ``` $ countryfetch germany -Country: Germany 🇩🇪 -Lat/Lng 51/9 -Population: 83,240,525 -Languages: German -Capital: Berlin -Capital Lat/Lng: 52.52/13.4 -Region: Europe -Subregion: Western Europe -Timezones: UTC+01:00 -Top Level Domain: .de -Currencies: Euro [€](EUR) +Country: Germany 🇩🇪 +Lat/Lng: 51/9 +Population: 83,240,525 +Languages: German +Capital: Berlin +Capital Lat/Lng: 52.52/13.4 +Region: Europe +Subregion: Western Europe +Timezones: UTC+01:00 +Top Level Domain: .de +Currencies: Euro [€](EUR) +``` + +## Flag ASCII Art + +[terminal_images](https://github.com/mjrlowe/terminal_images) library is used for image conversion. If you like my project, you should star +this project as well. Maybe contribute and help the author too! +To enable displaying ASCII art, run `countryfetch sync flags`. This may take a minute, depending on your network connection, as the +program fetches every flag image from countries data, converts them into ASCII art and stores them in "~/.cache/countryfetch/flags.json". +This library uses the unstable version of Deno, therefore if you get an error like "X is not a function" you should try passing the `--unstable` flag. +Due to this, consistency is not guaranteed. + +If for some reason you no longer want to display ASCII art, simply delete the flags.json in cache: + +``` +rm ~/.cache/countryfetch/flags.json ``` ## Troubleshooting @@ -78,7 +95,8 @@ you might have an outdated cache. Try running `countryfetch sync` to update the ## Would be nice to have in the future (a bit ambitious): -- Display country flag in ASCII format (like in neofetch). +- ~~Display country flag in ASCII format (like in neofetch).~~ +- Better arguments and options passing/handling. - Users can customize which information to display about country. - Get only one particular property about country (for example, get just the timezone of Netherlands). - Custom query builder that makes a direct call to api and returns a readable information. diff --git a/images/countryfetch.png b/images/countryfetch.png Binary files differindex 46eedc1..35a0292 100644 --- a/images/countryfetch.png +++ b/images/countryfetch.png diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..815a0c7 --- /dev/null +++ b/index.ts @@ -0,0 +1,3 @@ +import app from "./src/main.ts"; + +await app.run(); @@ -1,3 +1,3 @@ #!/bin/sh -deno install --allow-all "$@" ./main.ts
\ No newline at end of file +deno install --allow-all "$@" ./index.ts
\ No newline at end of file diff --git a/main.ts b/main.ts deleted file mode 100644 index fc26c20..0000000 --- a/main.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { App } from "./src/app.ts"; -import { Logger } from "./src/util/logger.ts"; -import { Cache } from "./src/util/cache.ts"; -import { Countries } from "./src/countries.ts"; - -// I wanted to get the hang of OOP but this feels regrettable -const logger = new Logger(); -const cache = new Cache(); -const countries = new Countries(cache, logger); -const app = new App(logger, countries); - -await app.run(); @@ -1,4 +1,4 @@ #!/bin/sh # Just a short-hand to run instead of typing it out in terminal during development -deno run --allow-all ./main.ts "$@"
\ No newline at end of file +deno run --allow-all --unstable "$@" ./index.ts
\ No newline at end of file @@ -20,10 +20,13 @@ export class App { this.logger.help(); break; case "sync": - await this.countries.sync({ force: true }); + await this.countries.sync({ + force: true, + flagAscii: Deno.args[1] === "flags", + }); break; case "random": - this.countries.print(this.countries.random()); + await this.countries.print(this.countries.random()); break; case "capital": const [, ...args] = Deno.args; @@ -34,7 +37,7 @@ export class App { this.logger.log(this.countries.find(Deno.args[1])); break; default: - this.countries.print(Deno.args.join(" ")); + await this.countries.print(Deno.args.join(" "), true); break; } } diff --git a/src/countries.ts b/src/countries.ts index 974edec..2485c49 100644 --- a/src/countries.ts +++ b/src/countries.ts @@ -5,17 +5,27 @@ import { Currencies, Languages, } from "./models/country.model.ts"; +import { FlagAscii } from "./models/flag-ascii.model.ts"; import { Cache } from "./util/cache.ts"; import { Logger } from "./util/logger.ts"; +import { ImageConverter } from "./util/image-converter.ts"; export class Countries { list: Country[] = []; names: string[] = []; + flags: FlagAscii[] = []; query = environment.queries; - constructor(private cache: Cache, private logger: Logger) {} + constructor( + private cache: Cache, + private logger: Logger, + private imageConverter: ImageConverter + ) {} - public async sync(config?: { force: boolean }): Promise<Country[]> { + public async sync(config?: { + force?: boolean; + flagAscii?: boolean; + }): Promise<Country[]> { if (this.shouldSync() || config?.force) { this.logger.alert( "Synchronizing countries database...", @@ -32,11 +42,21 @@ export class Countries { this.cache.saveJson("countries", countries); this.cache.saveTxt("last-synced", JSON.stringify(Date.now())); + if (config?.flagAscii) { + this.logger.alert( + "Generating ASCII art for each country flag. This may take a minute..." + ); + const flagStrings = await this.generateFlagImgs(countries); + this.flags = flagStrings; + this.cache.saveJson("flags", flagStrings); + } + this.logger.success( `Synced successfully: cache saved at ${environment.cacheDir}` ); } else { this.list = this.cache.readJson("countries") as Country[]; + this.flags = (this.cache.readJson("flags") as FlagAscii[]) || []; } this.names = this.list.map((c) => c.name.common); return this.list; @@ -94,10 +114,17 @@ export class Countries { return this.list.filter((country) => country.region === region); } - public print(name: string) { + public async print(name: string, flag?: boolean) { const country = this.find(name); const currencies = this.extractCurrencies(country.currencies); const languages = this.extractLanguages(country.languages); + const FlagAscii = this.flags.find( + (i) => i.countryName === country.name.common + ); + + if (FlagAscii) { + this.logger.log(FlagAscii.flagString[0]); + } this.logger.logCountry({ country: country.name.common, @@ -108,7 +135,7 @@ export class Countries { region: country.region, subregion: country.subregion, capitalLatLng: country.capitalInfo.latlng.join("/"), - timezones: country.timezones.join("\n\t\t\t "), + timezones: country.timezones.join(" | "), tld: country.tld.join(" | "), currencies, languages, @@ -135,7 +162,7 @@ export class Countries { const currency = currencies[currencyAbbr]; result.push(`${currency.name} [${currency.symbol}](${currencyAbbr})`); } - return result.join("\n\t\t\t "); + return result.join(" | "); } private extractLanguages(languages: Languages) { @@ -145,4 +172,18 @@ export class Countries { } return result.join(" | "); } + + private async generateFlagImgs(countries: Country[]): Promise<FlagAscii[]> { + const data = []; + for (const country of countries) { + // Replace png with jpg as the library used has trouble with png + const flagUrl = country.flags["png"].replace(".png", ".jpg"); + const flagString = await this.imageConverter.getImageStrings(flagUrl); + data.push({ + countryName: country.name.common, + flagString, + }); + } + return data; + } } diff --git a/src/environment/environment.ts b/src/environment/environment.ts index ef48c3d..18434f8 100644 --- a/src/environment/environment.ts +++ b/src/environment/environment.ts @@ -2,9 +2,15 @@ import home_dir from "https://deno.land/x/[email protected]/home_dir/mod.ts"; import { join } from "https://deno.land/[email protected]/path/mod.ts"; export const environment = { + // Backend from where the information is fetched baseUrl: "https://restcountries.com/v3.1/", + // Update cache if it has been longer than given DAYS since last sync syncInterval: 7, + // Directory where country information should be stored. cacheDir: join(home_dir() as string, ".cache", "countryfetch"), + // Determines the size of ASCII art flag size + flagWidth: 40, + // Fields that should be fetched from API queries: - "all?fields=name,capital,currencies,population,flag,languages,region,subregion,timezones,latlng,capitalInfo,tld", + "all?fields=name,capital,currencies,population,flag,languages,region,subregion,timezones,latlng,capitalInfo,tld,flags", }; diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..b5eaa1d --- /dev/null +++ b/src/main.ts @@ -0,0 +1,15 @@ +import { App } from "./app.ts"; +import { Logger } from "./util/logger.ts"; +import { Cache } from "./util/cache.ts"; +import { Countries } from "./countries.ts"; +import { ImageConverter } from "./util/image-converter.ts"; + +// I wanted to get the hang of OOP but this feels regrettable +const logger = new Logger(); +const cache = new Cache(); +const imageConverter = new ImageConverter(); + +const countries = new Countries(cache, logger, imageConverter); +const app = new App(logger, countries); + +export default app; diff --git a/src/models/country.model.ts b/src/models/country.model.ts index 61e0dfc..3ebe2b4 100644 --- a/src/models/country.model.ts +++ b/src/models/country.model.ts @@ -4,6 +4,8 @@ type CurrencyAbbr = string; type CurrencyInfo = { name: string; symbol: string }; type LangAbbr = string; +type ImageFormat = "png" | "svg"; +type Flags = Record<ImageFormat, string>; export type Currencies = Record<CurrencyAbbr, CurrencyInfo>; export type Languages = Record<LangAbbr, string>; @@ -35,4 +37,5 @@ export interface Country { latlng: number[]; }; tld: string[]; + flags: Flags; } diff --git a/src/models/FetchedCountry.model.ts b/src/models/fetched-country.model.ts index a53482e..a53482e 100644 --- a/src/models/FetchedCountry.model.ts +++ b/src/models/fetched-country.model.ts diff --git a/src/models/flag-ascii.model.ts b/src/models/flag-ascii.model.ts new file mode 100644 index 0000000..d11c9d1 --- /dev/null +++ b/src/models/flag-ascii.model.ts @@ -0,0 +1,4 @@ +export interface FlagAscii { + countryName: string; + flagString: string[]; +} diff --git a/src/util/image-converter.ts b/src/util/image-converter.ts new file mode 100644 index 0000000..17d070a --- /dev/null +++ b/src/util/image-converter.ts @@ -0,0 +1,22 @@ +import { + printImage, + getImageStrings, +} from "https://x.nest.land/[email protected]/mod.ts"; +import { environment } from "../environment/environment.ts"; + +export class ImageConverter { + width = environment.flagWidth; + public getImageStrings(path: string) { + return getImageStrings({ + path, + width: this.width, + }); + } + + public printImage(path: string) { + return printImage({ + path, + width: this.width, + }); + } +} diff --git a/src/util/logger.ts b/src/util/logger.ts index aaa92c5..58745b7 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -1,8 +1,8 @@ import * as nano from "https://deno.land/x/[email protected]/mod.ts"; -import { FetchedCountry } from "../models/FetchedCountry.model.ts"; +import { FetchedCountry } from "../models/fetched-country.model.ts"; export class Logger { - public log(...data: any) { + public log(data: any) { console.log(data); } @@ -15,33 +15,33 @@ export class Logger { } public error(...data: any) { - console.error(data); + console.error(nano.red(data)); } public logCountry(country: FetchedCountry) { console.log( - nano.cyan("\nCountry:\t\t"), + nano.cyan("Country:"), country.country, country.flag, - nano.green("\nLat/Lng\t\t\t"), + nano.green("\nLat/Lng:"), country.latlng, - nano.green("\nPopulation:\t\t"), + nano.green("\nPopulation:"), country.population, - nano.green("\nLanguages:\t\t"), + nano.green("\nLanguages:"), country.languages, - nano.green("\nCapital:\t\t"), + nano.green("\nCapital:"), country.capital, - nano.green("\nCapital Lat/Lng:\t"), + nano.green("\nCapital Lat/Lng:"), country.capitalLatLng, - nano.green("\nRegion:\t\t\t"), + nano.green("\nRegion:"), country.region, - nano.green("\nSubregion:\t\t"), + nano.green("\nSubregion:"), country.subregion, - nano.green("\nTimezones:\t\t"), + nano.green("\nTimezones:"), country.timezones, - nano.green("\nTop Level Domain:\t"), + nano.green("\nTop Level Domain:"), country.tld, - nano.green("\nCurrencies:\t\t"), + nano.green("\nCurrencies:"), country.currencies ); } @@ -63,6 +63,8 @@ export class Logger { "\nARGS:\n", "\tsync", "\n\t\tSynchronize database. Stores countries' data in ~/.cache/conntryfetch/countries.json.", + "\n\t\tPass additional argument 'sync flag' to fetch and convert flags in ASCII art.", + "\n\t\tAfter syncing flags, every countryfetch command will display flag ASCII art.", "\n", "\n\trandom", "\n\t\tPrint information about a random country.", |
