aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPridon Tetradze <[email protected]>2022-06-26 13:46:59 +0400
committerGitHub <[email protected]>2022-06-26 13:46:59 +0400
commit2e058526d896ab4ee5173ceb80d1248ac08ff594 (patch)
tree9550b812ea66f069396fef043cff1d10aeaec859
parent8626e2d8e258e11837d27a1e99ef3277e7dfdf3b (diff)
downloadcountryfetch-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.md44
-rw-r--r--images/countryfetch.pngbin1595036 -> 227638 bytes
-rw-r--r--index.ts3
-rwxr-xr-xinstall.sh2
-rw-r--r--main.ts12
-rwxr-xr-xrun.sh2
-rw-r--r--src/app.ts9
-rw-r--r--src/countries.ts51
-rw-r--r--src/environment/environment.ts8
-rw-r--r--src/main.ts15
-rw-r--r--src/models/country.model.ts3
-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.ts4
-rw-r--r--src/util/image-converter.ts22
-rw-r--r--src/util/logger.ts30
15 files changed, 155 insertions, 50 deletions
diff --git a/README.md b/README.md
index ef39e6e..822ba70 100644
--- a/README.md
+++ b/README.md
@@ -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.**
![](./images/countryfetch.png)
@@ -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
index 46eedc1..35a0292 100644
--- a/images/countryfetch.png
+++ b/images/countryfetch.png
Binary files differ
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();
diff --git a/install.sh b/install.sh
index 1b48f03..87478c2 100755
--- a/install.sh
+++ b/install.sh
@@ -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();
diff --git a/run.sh b/run.sh
index 022608a..bbd8b74 100755
--- a/run.sh
+++ b/run.sh
@@ -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
diff --git a/src/app.ts b/src/app.ts
index 7064d74..1994462 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -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.",