حتما خودتان بهتر میدانید که ما برنامهنویسان فراموشکار، جزئیات و آرگومانهای دستورات را به راحتی یادمان میرود. برای این منظور خیلی اوقات صفحات manual دستورها یا همان `man page` ها را چک میکنیم.
برای مثال با وارد کردن دستور `man ls` میتوانیم مستندات رسمی ابزار `ls` را در صفحهی دوست داشتنی ترمینالمان بخوانیم.
متاسفانه به جز فراموشکار، گاهی کمحوصله هم هستیم و دنبال کردن یک مستند رسمی طولانی میتواند جانفرسا باشد. مثلا `man bash` که توضیحات پوستهی `bash` را به ما نشان میدهد بسیار طولانی است و حدود پنج هزار خط است! برای حل این مشکل، [مستندات غیر رسمیای](https://tldr.sh/) تحت نام «طولانی بود نخوندم» یا "too long didn't read» یا به طور مخفف `TLDR` توسط کاربران ایجاد شد که نسخهی خلاصه شده و کاربردی از مستندات است و با مثالها و توضیحات و ... میتواند بسیار در زمان صرفهجویی کند. برای آشنایی اولیه با این مستندات میتوانید از [این لینک](https://tldr.inbrowser.app/) استفاده کنید.
اما استفاده از سایت شاید برای همهی کاربران گزینهی اول نباشد و بسیاری از ما دوست داریم یک برنامهی سمت کاربر داشته باشیم که به این شکل از آن استفاده کنیم: `tldr bash` و خودش از صفحات آنلاین خوانده و اطلاعات را در اختیار ما بگذارد. البته تنها مشکل ما این نیست. به جز این مورد، با توجه به سرعت محدود اینترنت، ما دوست داریم با استفاده از حافظهی نهان (کش) در زمان و مصرف اینترنت نیز صرفهجویی کند، به این ترتیب صفحاتی که تا کنون بازدید شدهاند را در خود ذخیره کند و در زمان استفاده، صفحهی ذخیره شده را بازگرداند.
کلاینت اصلی این نرمافزار با زبان `Javascript` و در محیط `Node.js` توسعه داده شده اما کلاینتهای دیگری نیز برای آن وجود دارد که میتوانید لیستی از آنها را [اینجا](https://github.com/tldr-pages/tldr/wiki/tldr-pages-clients) ملاحظه کنید. توصیه میشود یکی از آنها را نصب کنید و با آن کار کنید.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/53485/download_problem_initial_project/181480/) دانلود کنید. ساختار فایلهای پروژه به صورت زیر است:
```
.
├── go.mod
├── go.sum
├── tldr.go
├── db.go
├── main.go
└── main_sample_test.go
```
در فایل `main.go` چند تابع و متد وجود دارد که شما باید آنها را کامل کنید.
## آنچه باید پیادهسازی کنید
در فایل `tldr.go` یک اینترفیس وجود دارد که باید آن را پیادهسازی کنید. این اینترفیس دو متد دارد که در ادامه توضیح داده میشود.
```go tldr.go
type TLDRProvider interface {
Retrieve(string) string
List() []string
}
```
در فایل main.go، یک استراکت وجود دارد که قرار است اینترفیس گفته شده را پیادهسازی کند. همچنین یک تابع سازنده داریم
```go main.go
type TLDRDBCached struct {
}
func (t *TLDRDBCached) Retrieve(key string) string {
}
func (t *TLDRDBCached) List() []string {
}
func NewTLDRDBCached(nonCachedProvider TLDRProvider) TLDRProvider {
return nil
}
```
### متد Retrieve
در متد Retrieve، یک کلید که اسم همان ابزاری است که توضیحات بیشترش را میخواهیم به ما داده میشود و انتظار میرود توضیح و راهنمای کار با آن را به شکل یک رشته برگردانیم.
زمانی که یک کلید را دریافت میکنیم دو حالت دارد. حالت اول حالتی است که قبلا آن را دیدهایم و در کش ذخیره کردهایم، پس باید به سرعت و بدون معطلی از کش آن را خوانده و برگردانیم.
حالت دوم حالتی است که تا به حال این کلید را ندیدهایم، در این حالت باید با کمک منبع بیرونی یا همان provider (که خودش مثلا اینترنت دادهها را میخواند) و هم در کش ذخیره کند و هم برگرداند.
### متد List
این متد باید لیست **تمام مطالب**ی که در provider وجود دارند را برگرداند. به بیان دیگر این لیست یک رشته برمیگرداند شامل اسم تمام ابزارهایی که در `tldr` داکیومنت مربوط به آن وجود دارد. برای دسترسی به این لیست به سادگی میتوانید متد `List` را در تامینکننده فراخوانی کنید.
تنها نکتهای که وجود دارد این است که ترتیب این لیست باید تا حدی عوض شود. تفاوت به این صورت است که بین تمام مواردی که در لیست قرار میگیرند، در لیست خروجی شما باید اول اسم ابزارهایی که صفحهی آنها در کش موجود است آورده شود و در ادامه باقی ابزارها بیایند. مثلا اگر ابزار `zsh` تا کنون فراخوانی شده و در کش موجود است، اول این لیست `zsh` وجود داشته باشد و باقی ابزارها مثل `bash` قرار گیرند. ترتیب اعضایی که در کش هستند اهمیتی ندارد و ترتیب اعضایی که در کش نیستند نیز اهمیتی ندارد.
### سازندهی NewTLDRDBCached
این سازنده یک شی تامین کننده از جنس همین اینترفیس میگیرد و یک شی از نوع `TLDRDBCached` که به درستی مقداردهی کرده است برمیگرداند.
### روش ذخیرهسازی در حافظهی نهان
از آنجا که به فصل گرم سال نزدیک میشویم و ممکن است برق سیستم برود، نیاز داریم که این اطلاعات در یک پایگاهداده ذخیره شوند تا با خاموش و روشن شدن سیستم، همچنان دسترسی به اطلاعات از طریق دیتابیس موجود باشد. برای این منظور یک DBMS از نوع `postgres` تدارک دیدهایم که میتوانید از آن استفاده کنید. همچنین یک رابطه شی-ارتباط به نام `Gorm` هم برای شما تدارک دیده شده که می توانید برای سهولت بیشتر از آن استفاده کنید.
در فایل `db.go` یک تابع به نام `GetConnection` وجود دارد که یک شی از نوع `*gorm.DB` میدهد و میتوانید با آن به دیتابیس و امکانات `Gorm` دسترسی داشته باشید. در سیستم خودتان میتوانید اطلاعات اتصال به دیتابیس را مطابق سیستم خودتان بروز کنید ولی آن فایل را آپلود نخواهید کرد و سمت سرور فایل `db.go` مطابق کانفیگ سمت سرور موجود است.
در فایل `tldr.go` که قبلا بخشی از آن را دیدهایم، یک موجودیت نیز برای شما آورده شده است به نام `TLDREntity`:
```go tldr.go
type TLDREntity struct {
gorm.Model
Key string `gorm:"primaryKey;size:100"`
Val string `gorm:"size:1000"`
}
```
شما برای حل این سوال باید اطلاعات خود را در قالب شیای از این نوع در دیتابیس ذخیره کنید و حق تغییر در این شی را نیز ندارید.
پس دقت کنید که فقط اجازه دارید کش کردن داده را با استفاده از `Gorm` انجام دهید.
توجه کنید که در تستها بررسی میشود که جدول متناظر با این شی به طور مناسب پر شده باشد.
# تضمینها
+ تضمین میشود به متد `Retrieve` هیچگاه کلیدی که سمت سرور وجود ندارد پاس داده نمیشود.
+ تضمین میشود که اسم ابزار همواره زیر ۱۰۰ کارکتر طول دارد و تماما از کارکترهای انگلیسی تشکیل شده است.
+ تضمین میشود که متن توضیح ابزار همواره زیر ۱۰۰۰ کارکتر طول دارد و تماما از کارکترهای انگلیسی تشکیل شده است.
+ تضمین میشود که قبل از اجرای هر تست دیتابیس خالی است و فقط یک جدول خالی متناظر با `TLDREntity` وجود دارد.
+ تضمین میشود زمان اجرای تستها، فراخوانی `GetConnection` یک پوینتر به یک شی معتبر از نوع `gorm.DB`خواهد بود و با آن به شکل مناسب به دیتابیس دسترسی خواهید داشت.
+ تضمین میشود در `provider` حداقل داکیومنت مربوط به یک ابزار وجود دارد.
# آنچه باید آپلود کنید
پس از پیادهسازی توابع خواسته شده، فایل `main.go` را آپلود کنید.