Repository: mr-linch/go-tg (go)

Issue:
New InputFile from FS
```go
tg.NewInputFileFS(fsys fs.FS, path string)
```

Pull request description:
feat(tg): add NewInputFileFS
close #84 

Patch:
diff --git a/input_file.go b/input_file.go
index 200cc86..ada6cca 100644
--- a/input_file.go
+++ b/input_file.go
@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
+	"io/fs"
 	"os"
 	"path/filepath"
 )
@@ -87,3 +88,25 @@ func NewInputFileLocal(path string) (InputFile, error) {
 		file,
 	), nil
 }
+
+// NewInputFileFS creates the InputFile from provided FS and file path.
+//
+// Usage:
+//  //go:embed assets/*
+//  var assets embed.FS
+//  file, err := NewInputFileFS(assets, "images/test.png")
+//  if err != nil {
+//    return err
+//  }
+//  defer file.Close()
+func NewInputFileFS(fs fs.FS, path string) (InputFile, error) {
+	file, err := fs.Open(path)
+	if err != nil {
+		return InputFile{}, fmt.Errorf("open file: %w", err)
+	}
+
+	return NewInputFile(
+		filepath.Base(path),
+		file,
+	), nil
+}


Test patch:
diff --git a/input_file_test.go b/input_file_test.go
index a121160..a9d4c44 100644
--- a/input_file_test.go
+++ b/input_file_test.go
@@ -2,10 +2,13 @@ package tg
 
 import (
 	"encoding/json"
+	"io/fs"
 	"strings"
 	"testing"
+	"testing/fstest"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 )
 
 func TestInputFile_WithName(t *testing.T) {
@@ -55,6 +58,38 @@ func TestNewInputFileLocal(t *testing.T) {
 	}
 }
 
+type MockFS struct {
+	mock.Mock
+}
+
+func (mfs *MockFS) Open(path string) (fs.File, error) {
+	args := mfs.Called(path)
+
+	return args.Get(0).(fs.File), args.Error(1)
+}
+
+func TestNewInputFileFS(t *testing.T) {
+	fs := fstest.MapFS{
+		"hello.txt": {
+			Data: []byte("hello, world"),
+		},
+	}
+
+	t.Run("OK", func(t *testing.T) {
+		file, err := NewInputFileFS(fs, "hello.txt")
+		assert.NoError(t, err)
+		assert.NotNil(t, file)
+		assert.NoError(t, file.Close())
+	})
+
+	t.Run("NotExist", func(t *testing.T) {
+		file, err := NewInputFileFS(fs, "not-exist.txt")
+		assert.Error(t, err)
+		assert.Zero(t, file)
+		assert.Nil(t, file.Close())
+	})
+}
+
 func TestInputFile_MarshalJSON(t *testing.T) {
 	t.Run("WithoutAddr", func(t *testing.T) {
 		file := NewInputFile("test.txt", nil)