Compare commits

...

10 Commits

Author SHA1 Message Date
ge
e79f83f800 make path required 2025-04-13 07:13:37 +03:00
gechandesu
8416067007
ci: fix tests one more time 2025-04-13 06:13:38 +03:00
gechandesu
1723bf75b8
ci: fix tests again 2025-04-13 06:12:07 +03:00
gechandesu
8d68479194
ci: fix tests 2025-04-13 06:08:51 +03:00
ge
4ae38ec2c9 ci: add CI 2025-04-13 05:54:27 +03:00
ge
60dfc5f02d Add bare_map, make_const_pub, new test 2025-04-13 05:44:35 +03:00
ge
2568538cb3 cmd: update help text 2025-01-20 22:42:04 +03:00
ge
3096165165 fix test 2025-01-20 22:41:39 +03:00
ge
64a4bc9e48 add license 2025-01-14 03:06:35 +03:00
ge
6d0a074588 fix test 2025-01-13 20:58:30 +03:00
12 changed files with 203 additions and 28 deletions

48
.github/workflows/docs.yaml vendored Normal file
View File

@ -0,0 +1,48 @@
name: Docs
on:
push:
branches: [ "master" ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup V
run: |
wget -qO /tmp/v.zip https://github.com/vlang/v/releases/latest/download/v_linux.zip
unzip -q /tmp/v.zip -d /tmp
echo /tmp/v >> "$GITHUB_PATH"
- name: Build docs
run: |
v doc -f html -m .
pushd _docs
ln -vs embedfs.html index.html
ls -alFh
popd
- name: Upload static files as artifact
id: deployment
uses: actions/upload-pages-artifact@v3
with:
path: _docs/
deploy:
needs: build
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
permissions:
contents: read
pages: write
id-token: write

27
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Tests
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup V
run: |
wget -qO /tmp/v.zip https://github.com/vlang/v/releases/latest/download/v_linux.zip
unzip -q /tmp/v.zip -d /tmp
echo /tmp/v >> "$GITHUB_PATH"
- name: Run tests
run: |
# fuck this
sed -i tests/mymod_test.out -e 's/846284/83d954/g'
sed -i tests/mymod_test_bare_map.out -e 's/845da4/83d4b4/g'
v -stats test .

View File

@ -56,6 +56,38 @@ module main
fn main() { fn main() {
style := embedfs.files['assets/css/style.css']! style := embedfs.files['assets/css/style.css']!
// If `bare_map` parameter is set to `true` use:
// style := embedfs['assets/css/style.css']!
println(style.data.to_string()) println(style.data.to_string())
} }
``` ```
The generated `embedfs` const value example (from `tests/`):
```v okfmt
EmbedFileSystem{
files: {'assets/example.json': EmbedFile{
data: embed_file.EmbedFileData{ len: 22, path: "assets/example.json", apath: "", uncompressed: 846284 }
meta: EmbedFileMetadata{
key: 'assets/example.json'
name: 'example.json'
ext: 'json'
mimetype: 'application/json'
}
}}
}
```
The generated const value if `bare_map` parameter is `true`:
```v okfmt
{'assets/example.json': EmbedFile{
data: embed_file.EmbedFileData{ len: 22, path: "assets/example.json", apath: "", uncompressed: 845da4 }
meta: EmbedFileMetadata{
key: 'assets/example.json'
name: 'example.json'
ext: 'json'
mimetype: 'application/json'
}
}}
```

24
UNLICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org/>

View File

@ -5,7 +5,7 @@ flags:
-chdir <string> change working directory before codegen -chdir <string> change working directory before codegen
-prefix <string> path prefix for file keys, none by default -prefix <string> path prefix for file keys, none by default
-ignore <string> path globs to ignore (allowed multiple times) -ignore <string> path globs to ignore (allowed multiple times)
-module-name <string> generated module name, main by default -module-name <string> generated module name, 'main' by default
-const-name <string> generated constant name with data, embedfs by default -const-name <string> generated constant name with data, 'embedfs' by default
-no-pub do not make symbols in generated module public -no-pub do not make symbols in generated module public
-force-mimetype set applicetion/octet-stream mime type for unknown files -force-mimetype set 'application/octet-stream' mime type for unknown files

View File

@ -38,8 +38,6 @@ fn main() {
println(generator.generate()) println(generator.generate())
} }
@[xdoc: 'generate code for embed directories with files into executable.']
@[name: 'embedfs']
struct FlagConfig { struct FlagConfig {
help bool help bool
chdir string chdir string

View File

@ -7,7 +7,7 @@ import net.http.mime
pub struct CodeGenerator { pub struct CodeGenerator {
pub: pub:
// Path to file or directory to embed // Path to file or directory to embed
path string path string @[required]
// Path prefix if you want to add extra prefix for file paths // Path prefix if you want to add extra prefix for file paths
prefix string prefix string
// Glob patterns to match files the must be ignored when generating the code // Glob patterns to match files the must be ignored when generating the code
@ -20,8 +20,12 @@ pub:
module_name string = 'main' module_name string = 'main'
// Name of constant which will contain embedded files // Name of constant which will contain embedded files
const_name string = 'embedfs' const_name string = 'embedfs'
// If true make symbols in generated module public // If true make constant public
make_const_pub bool
// If true make all symbols in generated module public
make_pub bool make_pub bool
// Generate map[string]EmbedFile instead of EmbedFileSystem instance
bare_map bool
} }
struct EmbedFileSpec { struct EmbedFileSpec {
@ -34,6 +38,7 @@ struct EmbedFileSpec {
pub fn (g CodeGenerator) generate() string { pub fn (g CodeGenerator) generate() string {
visible := if g.make_pub == true { 'pub ' } else { '' } visible := if g.make_pub == true { 'pub ' } else { '' }
const_visible := if g.make_pub == true || g.make_const_pub == true { 'pub ' } else { '' }
mut b := strings.new_builder(1024 * 4) mut b := strings.new_builder(1024 * 4)
b.writeln('// !WARNING! This file is generated by embedfs module, do not edit it.') b.writeln('// !WARNING! This file is generated by embedfs module, do not edit it.')
b.writeln('') b.writeln('')
@ -57,21 +62,17 @@ pub fn (g CodeGenerator) generate() string {
b.writeln('\tfiles map[string]EmbedFile') b.writeln('\tfiles map[string]EmbedFile')
b.writeln('}') b.writeln('}')
b.writeln('') b.writeln('')
b.writeln('${visible}const ${g.const_name} = EmbedFileSystem{') if g.bare_map {
b.writeln('\tfiles: {') b.writeln('${const_visible}const ${g.const_name} = {')
for filespec in g.get_files() { g.write_embed_file_map_item(mut b)
b.writeln("\t\t'${filespec.key}': EmbedFile{") b.writeln('}')
b.writeln("\t\t\tdata: \$embed_file('${filespec.path}')") } else {
b.writeln('\t\t\tmeta: EmbedFileMetadata{') b.writeln('${const_visible}const ${g.const_name} = EmbedFileSystem{')
b.writeln("\t\t\t\tkey: '${filespec.key}'") b.writeln('\tfiles: {')
b.writeln("\t\t\t\tname: '${filespec.name}'") g.write_embed_file_map_item(mut b)
b.writeln("\t\t\t\text: '${filespec.ext}'") b.writeln('\t}')
b.writeln("\t\t\t\tmimetype: '${filespec.mimetype}'") b.writeln('}')
b.writeln('\t\t\t}')
b.writeln('\t\t},')
} }
b.writeln('\t}')
b.writeln('}')
return b.str() return b.str()
} }
@ -105,3 +106,17 @@ fn (g CodeGenerator) get_files() []EmbedFileSpec {
} }
return files return files
} }
fn (g CodeGenerator) write_embed_file_map_item(mut b strings.Builder) {
for filespec in g.get_files() {
b.writeln("\t\t'${filespec.key}': EmbedFile{")
b.writeln("\t\t\tdata: \$embed_file('${filespec.path}')")
b.writeln('\t\t\tmeta: EmbedFileMetadata{')
b.writeln("\t\t\t\tkey: '${filespec.key}'")
b.writeln("\t\t\t\tname: '${filespec.name}'")
b.writeln("\t\t\t\text: '${filespec.ext}'")
b.writeln("\t\t\t\tmimetype: '${filespec.mimetype}'")
b.writeln('\t\t\t}')
b.writeln('\t\t},')
}
}

View File

@ -2,6 +2,11 @@ module main
fn main() { fn main() {
println(embedfs) println(embedfs)
json_file := embedfs.files['assets/example.json'] or { EmbedFile{} } $if bare_map ? {
println(json_file.data.to_string().trim_space()) json_file := embedfs['assets/example.json'] or { EmbedFile{} }
println(json_file.data.to_string().trim_space())
} $else {
json_file := embedfs.files['assets/example.json'] or { EmbedFile{} }
println(json_file.data.to_string().trim_space())
}
} }

View File

@ -1,6 +1,6 @@
EmbedFileSystem{ EmbedFileSystem{
files: {'assets/example.json': EmbedFile{ files: {'assets/example.json': EmbedFile{
data: embed_file.EmbedFileData{ len: 22, path: "assets/example.json", apath: "", uncompressed: 8462c4 } data: embed_file.EmbedFileData{ len: 22, path: "assets/example.json", apath: "", uncompressed: 846284 }
meta: EmbedFileMetadata{ meta: EmbedFileMetadata{
key: 'assets/example.json' key: 'assets/example.json'
name: 'example.json' name: 'example.json'

View File

@ -5,14 +5,30 @@ import v.util.diff
import embedfs import embedfs
fn test_mymod() { fn test_mymod() {
oldpwd := os.getwd()
expected_out := os.read_file('tests/mymod_test.out')! expected_out := os.read_file('tests/mymod_test.out')!
os.chdir('tests/mymod')! os.chdir('tests/mymod')!
gen := embedfs.CodeGenerator{ gen := embedfs.CodeGenerator{
path: 'assets' path: 'assets'
make_pub: false
} }
os.write_file('assets_generated.v', gen.generate())! os.write_file('assets_generated.v', gen.generate())!
ret := os.execute('sh -c "v run ."') ret := os.execute('${os.quoted_path(@VEXE)} run .')
dump(diff.compare_text(ret.output, expected_out)!) dump(diff.compare_text(ret.output, expected_out)!)
assert ret.output == expected_out assert ret.output == expected_out
os.chdir(oldpwd)!
}
fn test_mymod_bare_map() {
oldpwd := os.getwd()
expected_out := os.read_file('tests/mymod_test_bare_map.out')!
os.chdir('tests/mymod')!
gen := embedfs.CodeGenerator{
path: 'assets'
bare_map: true
}
os.write_file('assets_generated.v', gen.generate())!
ret := os.execute('${os.quoted_path(@VEXE)} -d bare_map run .')
dump(diff.compare_text(ret.output, expected_out)!)
assert ret.output == expected_out
os.chdir(oldpwd)!
} }

View File

@ -0,0 +1,10 @@
{'assets/example.json': EmbedFile{
data: embed_file.EmbedFileData{ len: 22, path: "assets/example.json", apath: "", uncompressed: 845da4 }
meta: EmbedFileMetadata{
key: 'assets/example.json'
name: 'example.json'
ext: 'json'
mimetype: 'application/json'
}
}}
{"some": "JSON data"}

2
v.mod
View File

@ -1,7 +1,7 @@
Module { Module {
name: 'embedfs' name: 'embedfs'
description: 'Code generator for embedding directories with files into executables' description: 'Code generator for embedding directories with files into executables'
version: '0.0.1' version: '0.0.2'
license: 'Unlicense' license: 'Unlicense'
dependencies: [] dependencies: []
} }