feat: implement prototype

This commit is contained in:
thek4n 2026-06-03 00:40:13 +03:00
parent 4c5873ec96
commit 7d5e336868
8 changed files with 579 additions and 80 deletions

194
Cargo.lock generated Normal file
View File

@ -0,0 +1,194 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "askama"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf825125edd887a019d0a3a837dcc5499a68b0d034cc3eb594070c3e18addc"
dependencies = [
"askama_macros",
"itoa",
"percent-encoding",
"serde",
"serde_json",
]
[[package]]
name = "askama_derive"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1c7065972a130eafa84215f21352ae15b4a7393da48c1f5e103904490736738"
dependencies = [
"askama_parser",
"basic-toml",
"glob",
"memchr",
"proc-macro2",
"quote",
"rustc-hash",
"serde",
"serde_derive",
"syn",
]
[[package]]
name = "askama_macros"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e23b1d2c4bd39a41971f6124cef4cc6fd0540913ecb90919b69ab3bbe44ae1a"
dependencies = [
"askama_derive",
]
[[package]]
name = "askama_parser"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7db09fde9143e7ac4513358fb32ee32847125b63b18ea715afd487956da715da"
dependencies = [
"rustc-hash",
"serde",
"serde_derive",
"unicode-ident",
"winnow",
]
[[package]]
name = "basic-toml"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
dependencies = [
"serde",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "memchr"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc-hash"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thek4n-ru"
version = "0.1.0"
dependencies = [
"askama",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "winnow"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
dependencies = [
"memchr",
]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

View File

@ -5,3 +5,4 @@ edition = "2024"
authors = ["Vladislav Kan <thek4n@yandex.ru>"] authors = ["Vladislav Kan <thek4n@yandex.ru>"]
[dependencies] [dependencies]
askama = "0.16.0"

View File

@ -1,22 +1,30 @@
[page] [page]
page-title = "thek4n" title = "Hi, I'm Vlad"
page-meta-desc = "thek4n - developer" meta_title = "thek4n"
copyright-author = "thek4n" meta_description = "thek4n - developer"
description = "Developer"
copyright_author = "thek4n"
# --- # ---
# Site registered languages # Site registered languages
[languages] [[languages]]
en = "english" key = "en"
ru = "russian" title = "english"
[[languages]]
key = "ru"
title = "russian"
# --- # ---
[aboutme] [page.aboutme]
title.en = "About Me"
title.ru = "Обо Мне"
text.en = ''' text.en = '''
''' '''
@ -29,16 +37,22 @@ text.ru = '''
# --- # ---
[gpg] [page.gpg]
title.en = "GPG Public Key"
title.ru = "Публичный ключ GPG"
url = "/gpgkey.txt" url = "/gpgkey.txt"
showkey-text.en = "Show key" url_title = "thek4n.ru/gpgkey.txt"
showkey-text.ru = "Показать ключ" showkey_title.en = "Show key"
showkey_title.ru = "Показать ключ"
availat-text.en = "Available at" availat_title.en = "Available at"
availat-text.ru = "Доступен по" availat_title.ru = "Доступен по"
copied-text.en = "Copied!" copy_title.en = "Show key"
copied-text.ru = "Скопировано!" copy_title.ru = "Показать ключ"
copied_title.en = "Copied!"
copied_title.ru = "Скопировано!"
value = ''' value = '''
asdfasdf asdfasdf
@ -49,22 +63,31 @@ asdfasdf
# --- # ---
[page.projects]
title.en = "Projects"
title.ru = "Проекты"
[[projects]] [[projects]]
title = "ip.thek4n.ru" title = "ip.thek4n.ru"
url = "https://ip.thek4n.ru" url = "https://ip.thek4n.ru"
source_url = "https://gitea.thek4n.ru/thek4n/ip.thek4n.ru"
source_url_title = "thek4n/ip.thek4n.ru"
description.en = "check my ip service" description.en = "check my ip service"
description.ru = "сервис определения ip" description.ru = "сервис определения ip"
[[projects]] [[projects]]
title = "paste.thek4n.ru" title = "paste.thek4n.ru"
url = "https://paste.thek4n.ru" url = "https://paste.thek4n.ru"
source_url = "https://gitea.thek4n.ru/thek4n/paste.thek4n.ru"
source_url_title = "thek4n/paste.thek4n.ru"
description.en = "copy/paste and url shortener service" description.en = "copy/paste and url shortener service"
description.ru = "укоротитель ссылок" description.ru = "укоротитель ссылок"
[[projects]] [[projects]]
title = "thek4n.ru" title = "thek4n.ru"
url = "https://thek4n.ru" url = "https://thek4n.ru"
source_url = "https://gitea.thek4n.ru/thek4n/thek4n.ru"
source_url_title = "thek4n/thek4n.ru"
description.en = "this site source" description.en = "this site source"
description.ru = "исходники этого сайта" description.ru = "исходники этого сайта"
@ -72,20 +95,24 @@ description.ru = "исходники этого сайта"
# --- # ---
[page.contacts]
title.en = "Contacts"
title.ru = "Контакты"
[[contacts]] [[contacts]]
name.en = "Telegram" site_name.en = "Telegram"
name.ru = "Телеграм" site_name.ru = "Телеграм"
title = "@thek4n" title = "@thek4n"
url = "https://t.me/thek4n" url = "https://t.me/thek4n"
[[contacts]] [[contacts]]
name.en = "Github" site_name.en = "Github"
name.ru = "Github" site_name.ru = "Github"
title = "github.com/thek4n" title = "github.com/thek4n"
url = "https://github.com/thek4n" url = "https://github.com/thek4n"
[[contacts]] [[contacts]]
name.en = "Email" site_name.en = "Email"
name.ru = "Почта" site_name.ru = "Почта"
title = "thek4n@yandex.ru" title = "thek4n@yandex.ru"
url = "mailto:thek4n@yandex.ru" url = "mailto:thek4n@yandex.ru"

82
index.html Normal file
View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>thek4n</title>
<meta name="description" content="thek4n - developer">
<link rel="icon" href="favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<div class="top-controls">
<div>
<a id="lang-en" class="active" href="https://thek4n.ru/en">English</a>
</div>
</div>
<section>
<h1>TheK4n</h1>
<p><small>Developer</small></p>
</section>
<section>
<h1>Projects</h1>
<p>
<span><a href="https://paste.thek4n.ru">project</a></span>
:
<span><a href="https://gitea.thek4n.ru/thek4n/paste.thek4n.ru">thek4n/paste.thek4n.ru</a></span>
<span><small class="muted">(copy/paste service)</small>
</p>
</section>
<section>
<h1>About Me</h1>
<p>About me text</p>
</section>
<section>
<h2>GPG</h2>
<p>
<span>Available at</span>
<a href="/gpgkey.txt" target="_blank" rel="noopener noreferrer" >thek4n.ru/gpgkey.txt</a>
</p>
<details class="gpg-details">
<summary>
::marker
<span class="summary-row">
Show key
<a id="copy-gpg" class="copy-link" href="#" onclick="copyGPGKey()">Copy</a>
</span>
key....
</summary>
</details>
</section>
<section>
<h2>Contacts</h2>
<p>
<span>Telegram</span>
:
<span><a href="https://t.me/thek4n">@thek4n</a></span>
</p>
</section>
<footer>
<p>© 2026 Vladislav Kan &#60;thek4n@yandex.ru&#62;</p>
</footer>
</main>
<script src="script.js"></script>
</body>
</html>

View File

@ -1,3 +1,151 @@
fn main() { use askama::Template;
println!("Hello, world!");
#[derive(Template)]
#[template(path = "html_index.jinja2")]
struct HtmlPageTemplate<'a> {
title: &'a str,
description: &'a str,
copyright_author: &'a str,
base_url: &'a str,
meta: PageMeta<'a>,
year: &'a str,
aboutme: AboutMeSection<'a>,
gpg_section: GPGSection<'a>,
contacts_section: ContactsSection<'a>,
projects_section: ProjectsSection<'a>,
languages: Vec<Language<'a>>,
}
#[derive(Template)]
#[template(path = "text_index.jinja2", escape = "none")]
struct TextPageTemplate<'a> {
title: &'a str,
description: &'a str,
copyright_author: &'a str,
base_url: &'a str,
meta: PageMeta<'a>,
year: &'a str,
aboutme_section: AboutMeSection<'a>,
gpg_section: GPGSection<'a>,
contacts_section: ContactsSection<'a>,
projects_section: ProjectsSection<'a>,
languages: Vec<Language<'a>>,
}
struct PageMeta<'a> {
title: &'a str,
description: &'a str,
}
struct AboutMeSection<'a> {
title: &'a str,
text: &'a str,
}
struct GPGSection<'a> {
title: &'a str,
availat_title: &'a str,
url: &'a str,
url_title: &'a str,
showkey_title: &'a str,
copy_title: &'a str,
value: &'a str,
}
struct ContactsSection<'a> {
title: &'a str,
contacts: Vec<Contact<'a>>,
}
struct Contact<'a> {
site_name: &'a str,
url: &'a str,
title: &'a str,
}
struct ProjectsSection<'a> {
title: &'a str,
projects: Vec<Project<'a>>,
}
struct Project<'a> {
title: &'a str,
url: &'a str,
source_url: &'a str,
source_url_title: &'a str,
description: &'a str,
}
struct Language<'a> {
key: &'a str,
current: bool,
value: &'a str,
}
fn main() {
let meta = PageMeta{
title: "thek4n",
description: "thek4n - developer",
};
let aboutme_section = AboutMeSection{
title: "About Me",
text: "About me text",
};
let gpg_section = GPGSection{
title: "GPG Key",
availat_title: "Available at",
url: "/gpgkey.txt",
url_title: "thek4n.ru/gpgkey.txt",
showkey_title: "Show key",
copy_title: "Copy",
value: "key....",
};
let contacts = vec![Contact{
site_name: "Telegram",
url: "https://t.me/thek4n",
title: "@thek4n",
}];
let contacts_section = ContactsSection{
title: "Contacts",
contacts,
};
let projects = vec![Project{
title: "project",
url: "https://paste.thek4n.ru",
source_url: "https://gitea.thek4n.ru/thek4n/paste.thek4n.ru",
source_url_title: "thek4n/paste.thek4n.ru",
description: "copy/paste service",
}];
let projects_section = ProjectsSection{
title: "Projects",
projects,
};
let languages = vec![Language{
key: "en",
current: true,
value: "English",
}];
let index = TextPageTemplate{
title: "TheK4n",
description: "Developer",
copyright_author: "Vladislav Kan <thek4n@yandex.ru>",
base_url: "https://thek4n.ru",
meta,
year: "2026", // todo: get current year
aboutme_section,
gpg_section,
projects_section,
contacts_section,
languages,
};
println!("{}", index.render().unwrap());
} }

View File

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ meta.title }}</title>
<meta name="description" content="{{ meta.description }}">
<link rel="icon" href="favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<div class="top-controls">
<div>
{% for lang in languages %}
<a id="lang-{{ lang.key }}" {% if lang.current %} class="active" {% endif %} href="{{ base_url }}?lang={{ lang.key }}">{{ lang.value }}</a>
{% endfor %}
</div>
</div>
<section>
<h1>{{ title }}</h1>
<p><small>{{ description }}</small></p>
</section>
<section>
<h1>{{ projects_section.title }}</h1>
{% for project in projects_section.projects %}
<p>
<span><a href="{{ project.url }}">{{ project.title }}</a></span>
:
<span><a href="{{ project.source_url }}">{{ project.source_url_title }}</a></span>
<span><small class="muted">({{ project.description }})</small>
</p>
{% endfor %}
</section>
<section>
<h1>{{ aboutme.title }}</h1>
<p>{{ aboutme.text }}</p>
</section>
<section>
<h2>{{ gpg_section.title }}</h2>
<p>
<span>{{ gpg_section.availat_title }}</span>
<a href="{{ gpg_section.url }}" target="_blank" rel="noopener noreferrer" >{{ gpg_section.url_title }}</a>
</p>
<details class="gpg-details">
<summary>
<span class="summary-row">
{{ gpg_section.showkey_title }}
<a id="copy-gpg" class="copy-link" href="#" onclick="copyGPGKey()">{{ gpg_section.copy_title }}</a>
</span>
{{ gpg_section.value }}
</summary>
</details>
</section>
<section>
<h2>{{ contacts_section.title }}</h2>
{% for contact in contacts_section.contacts %}
<p>
<span>{{ contact.site_name }}</span>
:
<span><a href="{{ contact.url }}">{{ contact.title }}</a></span>
</p>
{% endfor %}
</section>
<footer>
<p>© {{ year }} {{ copyright_author }}</p>
</footer>
</main>
<script src="script.js"></script>
</body>
</html>

View File

@ -1,58 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ PAGE TITLE }}</title>
<meta name="description" content="{{ META DESCRIPTION }}">
<link rel="icon" href="favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<div class="top-controls">
<div>
<a id="lang-en" class="active" href="{{ URL }}/en">en</a>
<a id="lang-ru" href="{{ URL }}/ru">ru</a>
{{ LANGUAGES }}
</div>
</div>
<section>
<h1 data-lang="en">{{ PAGE_TITLE }}</h1>
<p data-lang="en"><small>{{ PAGE_DESCRIPTION }}</small></p>
</section>
<section>
<h1 data-lang="en">Projects</h1>
<p data-lang="en"><small class="muted">service desc</small></p>
</section>
<section>
<h2>GPG Public Key</h2>
<p>
<span data-lang="en">Available at:</span>
<a href="{{ GPG KEY URL }}" target="_blank" rel="noopener noreferrer" >{{ GPG KEY URL }}</a>
</p>
<details class="gpg-details">
<summary data-lang="en">
::marker
<span class="summary-row">
Show key
<a id="copy-gpg" class="copy-link" href="#" onclick="copyGPGKey()">copy to clipboard</a>
</span>
</summary>
</details>
</section>
<footer>
<p>© {{ YEAR }} {{ COPYRIGHT_AUTHOR }}</p>
</footer>
</main>
<script src="script.js"></script>
</body>
</html>

View File

@ -0,0 +1,24 @@
{{ title }} - {{ description }}
{{ projects_section.title }}:
{%- for project in projects_section.projects %}
> {{ project.title }} ({{project.description}}) - {{ project.url }}
{%- endfor %}
{{ contacts_section.title }}:
{%- for contact in contacts_section.contacts %}
> {{ contact.site_name }}: {{ contact.title }}
{%- endfor %}
{{ gpg_section.title }}: {{ base_url }}{{ gpg_section.url }}
{{ aboutme_section.title }}:
{{ aboutme_section.text }}
© {{ year }} {{ copyright_author }}