├── src ├── mod.rs ├── logger │ ├── mod.rs │ └── logger.rs ├── command │ ├── mod.rs │ └── command.rs ├── config │ ├── mod.rs │ ├── package_json.rs │ └── aktr_config.rs └── main.rs ├── .gitignore ├── Cargo.toml ├── example ├── package.json └── .aktr.toml ├── README.md └── Cargo.lock /src/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod config; -------------------------------------------------------------------------------- /src/logger/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod logger; -------------------------------------------------------------------------------- /src/command/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod command; -------------------------------------------------------------------------------- /src/config/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod aktr_config; 2 | pub mod package_json; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .env 3 | 4 | # Added by cargo 5 | # 6 | # already existing elements were commented out 7 | 8 | #/target 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aktr" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | toml = "0.8.14" 8 | serde = "1.0.203" 9 | serde_derive = "1.0.203" 10 | colored = "2.0" 11 | serde_json = "1.0.117" 12 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "module": "index.ts", 4 | "type": "module", 5 | "devDependencies": { 6 | "@types/bun": "latest" 7 | }, 8 | "peerDependencies": { 9 | "typescript": "^5.0.0" 10 | } 11 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aktr 2 | 3 | ## One Class 4 | Aktr is a Javascript meta-framework with one goal: Write a single class, and let Aktr generate all of the code needed to generate a server-side route, handle middleware, and deliver stlyed/interactive html to the client. -------------------------------------------------------------------------------- /example/.aktr.toml: -------------------------------------------------------------------------------- 1 | [entrypoint] 2 | root = "./index.tsx" 3 | 4 | [src] 5 | dir = "aktr/" 6 | lib = "aktr/lib/" 7 | client = "aktr/client/" 8 | server = "aktr/server/" 9 | shared = "aktr/shared/" 10 | package_json = "package.json" 11 | config = ".aktr.toml" 12 | -------------------------------------------------------------------------------- /src/command/command.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | 4 | 5 | 6 | pub struct CommandCollector { 7 | args: Vec, 8 | } 9 | 10 | impl CommandCollector { 11 | 12 | pub fn new() -> Self { 13 | CommandCollector { 14 | args: env::args().collect(), 15 | } 16 | } 17 | 18 | pub fn get(&self, index: usize) -> &str { 19 | if index < self.args.len() { 20 | return &self.args[index]; 21 | } 22 | "" 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /src/logger/logger.rs: -------------------------------------------------------------------------------- 1 | 2 | use colored::*; 3 | 4 | pub struct AktrLogger {} 5 | 6 | impl AktrLogger { 7 | 8 | pub fn new() -> Self { 9 | AktrLogger {} 10 | } 11 | 12 | pub fn err(message: String) { 13 | println!("{}", format!("Error: {}", message).red().bold()); 14 | } 15 | 16 | pub fn info(message: &str) { 17 | println!("{}", format!("Info: {}", message).green().bold()); 18 | } 19 | 20 | pub fn warn(message: &str) { 21 | println!("{}", format!("Warning: {}", message).yellow().bold()); 22 | } 23 | 24 | pub fn general(title: &str, message: &str) { 25 | println!("{}", format!("{}: {}", title.cyan().bold(), message)); 26 | } 27 | 28 | pub fn title_line(title: &str) { 29 | println!("{}", format!("{}", title.cyan().bold())); 30 | } 31 | 32 | pub fn indented_line(message: &str) { 33 | println!("{}", format!("{} {}", "-", message)); 34 | } 35 | 36 | pub fn skip_line() { 37 | println!(""); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/config/package_json.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, fs::File, io::{Error, ErrorKind, Write}}; 2 | use std::path::Path; 3 | use serde_derive::{Deserialize, Serialize}; 4 | 5 | 6 | #[derive(Serialize, Deserialize, Debug)] 7 | pub struct PackageJson { 8 | name: String, 9 | module: String, 10 | #[serde(rename = "type")] 11 | package_type: String, 12 | #[serde(rename = "devDependencies")] 13 | dev_dependencies: HashMap, 14 | #[serde(rename = "peerDependencies")] 15 | peer_dependencies: HashMap, 16 | } 17 | 18 | impl PackageJson { 19 | 20 | pub fn default() -> Self { 21 | let mut dev_dependencies = HashMap::new(); 22 | dev_dependencies.insert("@types/bun".to_string(), "latest".to_string()); 23 | 24 | let mut peer_dependencies = HashMap::new(); 25 | peer_dependencies.insert("typescript".to_string(), "^5.0.0".to_string()); 26 | 27 | PackageJson { 28 | name: "example".to_string(), 29 | module: "index.ts".to_string(), 30 | package_type: "module".to_string(), 31 | dev_dependencies: dev_dependencies, 32 | peer_dependencies: peer_dependencies, 33 | } 34 | } 35 | 36 | pub fn write_to_file(self: &Self, path: &str) -> Result<(), Error> { 37 | if let Some(parent) = Path::new(path).parent() { 38 | std::fs::create_dir_all(parent)?; 39 | } 40 | let mut file = File::create(path)?; 41 | let file_content = serde_json::to_string_pretty(self) 42 | .map_err(|_| Error::new(ErrorKind::InvalidData, "could not serialize package.json"))?; 43 | file.write_all(file_content.as_bytes())?; 44 | Ok(()) 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/config/aktr_config.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::{io::ErrorKind, path::Path}; 3 | use std::io::Error; 4 | 5 | use serde_derive::{Deserialize, Serialize}; 6 | 7 | pub const DEFAULT_AKTR_CONFIG_PATH: &str = ".aktr.toml"; 8 | 9 | #[derive(Debug, Serialize, Deserialize)] 10 | pub struct AktrConfigEntryPoint { 11 | pub root: String 12 | } 13 | 14 | impl AktrConfigEntryPoint { 15 | 16 | pub fn default() -> Self { 17 | AktrConfigEntryPoint { 18 | root: String::from("./index.tsx") 19 | } 20 | } 21 | 22 | } 23 | 24 | #[derive(Debug, Serialize, Deserialize)] 25 | pub struct AktrConfigSrc { 26 | pub dir: String, 27 | pub lib: String, 28 | pub client: String, 29 | pub server: String, 30 | pub shared: String, 31 | pub package_json: String, 32 | pub config: String, 33 | } 34 | 35 | impl AktrConfigSrc { 36 | 37 | pub fn default() -> Self { 38 | AktrConfigSrc { 39 | dir: String::from("aktr/"), 40 | lib: String::from("aktr/lib/"), 41 | client: String::from("aktr/client/"), 42 | server: String::from("aktr/server/"), 43 | shared: String::from("aktr/shared/"), 44 | package_json: String::from("package.json"), 45 | config: String::from(".aktr.toml"), 46 | } 47 | } 48 | 49 | } 50 | 51 | 52 | 53 | #[derive(Debug, Serialize, Deserialize)] 54 | pub struct AktrConfig { 55 | pub entrypoint: AktrConfigEntryPoint, 56 | pub src: AktrConfigSrc 57 | } 58 | 59 | impl AktrConfig { 60 | 61 | pub fn default() -> Self { 62 | AktrConfig { 63 | entrypoint: AktrConfigEntryPoint::default(), 64 | src: AktrConfigSrc::default(), 65 | } 66 | } 67 | 68 | pub fn from_file() -> Result { 69 | let path = Path::new("./.aktr.toml"); 70 | let config_contents = fs::read_to_string(path); 71 | if config_contents.is_err() { 72 | return Err(Error::new(ErrorKind::NotFound, ".aktr.toml not found")); 73 | } 74 | let config = toml::from_str(config_contents.unwrap().as_str()); 75 | if config.is_err() { 76 | return Err(Error::new(ErrorKind::Other, "failed to parse .aktr.toml")); 77 | } 78 | Ok(config.unwrap()) 79 | } 80 | 81 | pub fn init_config_file(&self, init_destination: String) -> Result<&Self, Error> { 82 | if Path::new(&init_destination).exists() { 83 | return Err(Error::new(ErrorKind::AlreadyExists, ".aktr.toml already exists")); 84 | } 85 | if let Some(parent) = Path::new(&init_destination).parent() { 86 | fs::create_dir_all(parent)?; 87 | } 88 | let toml_string = toml::to_string(&self); 89 | if toml_string.is_err() { 90 | return Err(Error::new(ErrorKind::Other, "failed to serialize config to toml")); 91 | } 92 | let file = fs::write(init_destination, toml_string.unwrap()); 93 | if file.is_err() { 94 | return Err(Error::new(ErrorKind::Other, "failed to write .aktr.toml")); 95 | } 96 | Ok(self) 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | mod config; 3 | mod logger; 4 | mod command; 5 | 6 | use config::aktr_config::AktrConfig; 7 | use config::aktr_config::DEFAULT_AKTR_CONFIG_PATH; 8 | use config::package_json::PackageJson; 9 | use logger::logger::AktrLogger; 10 | use command::command::CommandCollector; 11 | 12 | 13 | 14 | 15 | fn main() { 16 | 17 | let commands = CommandCollector::new(); 18 | 19 | if commands.get(1) == "init" { 20 | handle_init(); 21 | return; 22 | } 23 | 24 | if commands.get(1) == "generate" { 25 | handle_generate(); 26 | return; 27 | } 28 | 29 | if commands.get(1) == "" { 30 | println!(""); 31 | AktrLogger::title_line("aktr - a rust-powered typescript meta-framework"); 32 | AktrLogger::skip_line(); 33 | AktrLogger::title_line("usage:"); 34 | AktrLogger::indented_line("aktr init [path] create a .aktr.toml file in the current directory or in the specified path"); 35 | AktrLogger::indented_line("aktr generate generate typescript code from rust"); 36 | return; 37 | 38 | } 39 | 40 | 41 | } 42 | 43 | 44 | fn handle_init() { 45 | AktrLogger::skip_line(); 46 | AktrLogger::title_line("aktr - a rust-powered typescript meta-framework"); 47 | let commands = CommandCollector::new(); 48 | let mut third_command = commands.get(2); 49 | if third_command == "" { 50 | third_command = DEFAULT_AKTR_CONFIG_PATH; 51 | } 52 | let aktr_config_dest = format!("./{}/{}", third_command, DEFAULT_AKTR_CONFIG_PATH); 53 | AktrLogger::indented_line(format!("creating {}", DEFAULT_AKTR_CONFIG_PATH).as_str()); 54 | let default_config = AktrConfig::default(); 55 | let config = default_config.init_config_file(aktr_config_dest.to_string()); 56 | if config.is_err() { 57 | let err = config.err().unwrap(); 58 | AktrLogger::err(err.to_string()); 59 | return; 60 | } 61 | let config = config.unwrap(); 62 | AktrLogger::indented_line(format!("{} created", DEFAULT_AKTR_CONFIG_PATH).as_str()); 63 | if third_command != DEFAULT_AKTR_CONFIG_PATH { 64 | AktrLogger::indented_line(format!("run 'cd {}', then 'bun install' to install dependencies", third_command).as_str()); 65 | } else { 66 | AktrLogger::indented_line("run 'bun install' to install dependencies"); 67 | } 68 | AktrLogger::indented_line("finally, run 'aktr generate' to generate typescript code from rust"); 69 | let package_json = PackageJson::default(); 70 | let package_json_write = package_json.write_to_file(format!("./{}/{}", third_command, config.src.package_json).as_str()); 71 | if package_json_write.is_err() { 72 | let err = package_json_write.err().unwrap(); 73 | AktrLogger::err(err.to_string()); 74 | return; 75 | } 76 | AktrLogger::skip_line(); 77 | } 78 | 79 | fn handle_generate() { 80 | let commands = CommandCollector::new(); 81 | AktrLogger::info("generating aktr output files"); 82 | let config = AktrConfig::from_file(); 83 | if config.is_err() { 84 | let err = config.err().unwrap(); 85 | AktrLogger::err(err.to_string()); 86 | return; 87 | } 88 | let config = config.unwrap(); 89 | let src_dir = config.src.dir; 90 | AktrLogger::info(format!("src directory: {}", src_dir).as_str()); 91 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aktr" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "colored", 10 | "serde", 11 | "serde_derive", 12 | "serde_json", 13 | "toml", 14 | ] 15 | 16 | [[package]] 17 | name = "colored" 18 | version = "2.1.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 21 | dependencies = [ 22 | "lazy_static", 23 | "windows-sys", 24 | ] 25 | 26 | [[package]] 27 | name = "equivalent" 28 | version = "1.0.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 31 | 32 | [[package]] 33 | name = "hashbrown" 34 | version = "0.14.5" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 37 | 38 | [[package]] 39 | name = "indexmap" 40 | version = "2.2.6" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 43 | dependencies = [ 44 | "equivalent", 45 | "hashbrown", 46 | ] 47 | 48 | [[package]] 49 | name = "itoa" 50 | version = "1.0.11" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 53 | 54 | [[package]] 55 | name = "lazy_static" 56 | version = "1.4.0" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 59 | 60 | [[package]] 61 | name = "memchr" 62 | version = "2.7.4" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 65 | 66 | [[package]] 67 | name = "proc-macro2" 68 | version = "1.0.85" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" 71 | dependencies = [ 72 | "unicode-ident", 73 | ] 74 | 75 | [[package]] 76 | name = "quote" 77 | version = "1.0.36" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 80 | dependencies = [ 81 | "proc-macro2", 82 | ] 83 | 84 | [[package]] 85 | name = "ryu" 86 | version = "1.0.18" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 89 | 90 | [[package]] 91 | name = "serde" 92 | version = "1.0.203" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" 95 | dependencies = [ 96 | "serde_derive", 97 | ] 98 | 99 | [[package]] 100 | name = "serde_derive" 101 | version = "1.0.203" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" 104 | dependencies = [ 105 | "proc-macro2", 106 | "quote", 107 | "syn", 108 | ] 109 | 110 | [[package]] 111 | name = "serde_json" 112 | version = "1.0.117" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" 115 | dependencies = [ 116 | "itoa", 117 | "ryu", 118 | "serde", 119 | ] 120 | 121 | [[package]] 122 | name = "serde_spanned" 123 | version = "0.6.6" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" 126 | dependencies = [ 127 | "serde", 128 | ] 129 | 130 | [[package]] 131 | name = "syn" 132 | version = "2.0.66" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" 135 | dependencies = [ 136 | "proc-macro2", 137 | "quote", 138 | "unicode-ident", 139 | ] 140 | 141 | [[package]] 142 | name = "toml" 143 | version = "0.8.14" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" 146 | dependencies = [ 147 | "serde", 148 | "serde_spanned", 149 | "toml_datetime", 150 | "toml_edit", 151 | ] 152 | 153 | [[package]] 154 | name = "toml_datetime" 155 | version = "0.6.6" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" 158 | dependencies = [ 159 | "serde", 160 | ] 161 | 162 | [[package]] 163 | name = "toml_edit" 164 | version = "0.22.14" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" 167 | dependencies = [ 168 | "indexmap", 169 | "serde", 170 | "serde_spanned", 171 | "toml_datetime", 172 | "winnow", 173 | ] 174 | 175 | [[package]] 176 | name = "unicode-ident" 177 | version = "1.0.12" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 180 | 181 | [[package]] 182 | name = "windows-sys" 183 | version = "0.48.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 186 | dependencies = [ 187 | "windows-targets", 188 | ] 189 | 190 | [[package]] 191 | name = "windows-targets" 192 | version = "0.48.5" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 195 | dependencies = [ 196 | "windows_aarch64_gnullvm", 197 | "windows_aarch64_msvc", 198 | "windows_i686_gnu", 199 | "windows_i686_msvc", 200 | "windows_x86_64_gnu", 201 | "windows_x86_64_gnullvm", 202 | "windows_x86_64_msvc", 203 | ] 204 | 205 | [[package]] 206 | name = "windows_aarch64_gnullvm" 207 | version = "0.48.5" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 210 | 211 | [[package]] 212 | name = "windows_aarch64_msvc" 213 | version = "0.48.5" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 216 | 217 | [[package]] 218 | name = "windows_i686_gnu" 219 | version = "0.48.5" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 222 | 223 | [[package]] 224 | name = "windows_i686_msvc" 225 | version = "0.48.5" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 228 | 229 | [[package]] 230 | name = "windows_x86_64_gnu" 231 | version = "0.48.5" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 234 | 235 | [[package]] 236 | name = "windows_x86_64_gnullvm" 237 | version = "0.48.5" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 240 | 241 | [[package]] 242 | name = "windows_x86_64_msvc" 243 | version = "0.48.5" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 246 | 247 | [[package]] 248 | name = "winnow" 249 | version = "0.6.13" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" 252 | dependencies = [ 253 | "memchr", 254 | ] 255 | --------------------------------------------------------------------------------