├── .dockerignore ├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── config.js ├── content ├── chapter-1.md ├── chapter-1 │ ├── 1-sejarah.md │ ├── 2-developer-tools.md │ └── 3-getting-started.md ├── chapter-2.md ├── chapter-2 │ ├── 1-variables-and-mutability.md │ ├── 10-lifetime.md │ ├── 11-common-collections.md │ ├── 12-error-handling.md │ ├── 13-generic-types-and-traits.md │ ├── 2-data-types.md │ ├── 3-comments.md │ ├── 4-control-flow.md │ ├── 5-perulangan.md │ ├── 6-struct.md │ ├── 7-enum-and-pattern-matching.md │ ├── 8-ownership.md │ └── 9-slices.md ├── chapter-3.md ├── chapter-3 │ ├── 1-cargo.md │ └── 2-crates.md └── index.md ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── netlify.toml ├── package-lock.json ├── package.json ├── src ├── CommunityAuthor.js ├── GithubLink.js ├── YoutubeEmbed.js ├── components │ ├── Header.js │ ├── NextPrevious.js │ ├── images │ │ ├── closed.js │ │ ├── github.svg │ │ ├── help.svg │ │ ├── logo.svg │ │ ├── opened.js │ │ └── twitter.svg │ ├── index.js │ ├── layout.js │ ├── link.js │ ├── mdxComponents │ │ ├── LiveProvider.js │ │ ├── anchor.js │ │ ├── codeBlock.js │ │ ├── index.js │ │ └── loading.js │ ├── rightSidebar.js │ ├── search │ │ ├── hitComps.js │ │ ├── index.js │ │ ├── input.js │ │ └── styles.js │ ├── sidebar │ │ ├── index.js │ │ ├── tree.js │ │ └── treeNode.js │ ├── styles.css │ ├── theme.js │ └── themeProvider.js ├── custom-sw-code.js ├── html.js ├── pwa-512.png ├── templates │ └── docs.js └── utils │ └── algolia.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public 3 | .cache 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [{*.json,*.yml}] 15 | indent_size = 2 16 | indent_style = space 17 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | }, 5 | "globals": { 6 | "graphql": false 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 10, 10 | "sourceType": "module", 11 | "ecmaFeatures": { 12 | "jsx": true 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | public 2 | .cache 3 | node_modules 4 | *DS_Store 5 | *.env 6 | 7 | .idea/ 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:buster 2 | 3 | # Create app directory 4 | WORKDIR /app 5 | 6 | # Install app dependencies 7 | # RUN npm -g install serve 8 | RUN npm -g install gatsby-cli 9 | 10 | COPY package*.json ./ 11 | 12 | RUN npm ci 13 | 14 | # Bundle app source 15 | COPY . . 16 | 17 | # Build static files 18 | RUN npm run build 19 | 20 | # serve on port 8080 21 | # CMD ["serve", "-l", "tcp://0.0.0.0:8080", "public"] 22 | CMD ["gatsby", "serve", "--verbose", "--prefix-paths", "-p", "8080", "--host", "0.0.0.0"] 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Hasura 4 | Copyright (c) 2020 evilfactorylabs 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Belajar Rust 2 | 3 | Repositori ini berisi sumber kode (dan konten) dari *buku* Belajar Rust yang ditulis menggunakan Bahasa Indonesia 4 | dan ditujukan untuk siapa saja yang tertarik; penasaran, dan ingin belajar bersama seputar bahasa pemrograman 5 | bernama Rust. 6 | 7 | Buku ini masih dalam proses penulisan & penyuntingan, dan untuk sekarang bisa dilihat pratinjau nya 8 | [disini](https://evl-rust-ebook-draft.netlify.app/). 9 | 10 | Salah satu tujuan mengapa proses pembuatan & penyelesaian buku ini dilakukan di publik adalah agar setiap orang 11 | dapat memberikan masukan, peningkatan, koreksi, ide, dsb agar membuat buku ini menjadi lebih baik lagi untuk semua 12 | kalangan. 13 | 14 | ## Motivasi 15 | 16 | Rust adalah bahasa pemrograman yang tangguh, cepat, dan produktif. Rust menawarkan keamanan dari sisi memori, terlebih 17 | (per 11 Februari 2019) sekitar 70% celah keamanan yang ada di Microsoft adalah di keamanan memori[1], mengingat Microsoft 18 | sangat banyak menulis kode menggunakan C dan C++ untuk produk nya yang mana untuk manajemen memori dilakukan oleh 19 | pengembang nya itu sendiri secara manual. 20 | 21 | Rust menawarkan 'keamanan memori' salah satunya dengan konsep *ownership* nya yang lumayan unik. Karena keunikan inilah 22 | mengapa Rust relatif sulit dimengerti (dan dipelajari) karena mungkin banyak yang kurang familiar dengan apa yang Rust 23 | tawarkan seputar manajemen memori ini. 24 | 25 | Pastinya masih banyak lagi fitur-fitur serta nilai tambah yang ditawarkan oleh Rust, dan dengan ditulis nya buku ini, semoga kita 26 | semua dapat mempelajari & memahami bahasa pemrograman Rust dengan lebih mudah lagi, dan semoga menjadi lebih asik juga! 27 | 28 | Untuk siapapun, terlepas dari lama pengalaman dan latar belakang yang dimiliki. 29 | 30 | ## Pemelihara 31 | 32 | Untuk sekarang, penulis utama dari buku ini adalah [Adiatma Kamarudin](https://github.com/adiatma) dan dibantu oleh kawan-kawan 33 | berikut khususnya dalam proses penyuntingan: 34 | 35 | - [Kevin](https://github.com/kevanantha), [@evilfactorylabs](https://github.com/evilfactorylabs) 36 | - [Rin](https://github.com/ri7nz), [@evilfactorylabs](https://github.com/evilfactorylabs) 37 | 38 | Konten-konten yang ada ditulis menggunakan format Markdown, dan setiap orang dapat berkontribusi (serta berkolaborasi) dalam setiap proses 39 | dan pekerjaan ada yang ada. 40 | 41 | 42 | ## Berkontribusi 43 | 44 | Jika kamu menemukan kesalahan pengetikan/format, bias, struktur yang kurang rapih, dsb kamu bisa melakukan *Pull Request* 45 | dan utarakan apa yang ingin kamu sampaikan. Dan akan lebih baik bila disertakan dengan rujukan dan konteks agar proses menjadi 46 | lebih mudah untuk dilanjutkan. 47 | 48 | Dan jika umpan balik yang kamu miliki sekiranya ingin diutarakan secara personal, kamu bisa menghubungi 49 | [penulis](https://github.com/adiatma) dengan mengirim pesan elektronik atau menghubungi via akun Twitter nya. 50 | 51 | ## Lisensi 52 | 53 | Kode yang ada di repositori ini diterbitkan diatas lisensi MIT dan untuk kontennya menggunakan CC BY-NC-SA 4.0, yang mana 54 | menggunakan lisensi yang sangat permisif namun khusus untuk konten tidak digunakan untuk kepentingan komersil. 55 | 56 | [1]: https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/ 57 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | gatsby: { 3 | pathPrefix: '/', 4 | siteUrl: 'https://rust.evilfactory.id', 5 | gaTrackingId: '' 6 | }, 7 | header: { 8 | logo: '', 9 | logoLink: 'https://rust.evilfactory.id', 10 | title: 'Belajar Rust', 11 | githubUrl: 'https://github.com/evilfactorylabs/handbook', 12 | helpUrl: '', 13 | tweetText: '', 14 | links: [{ text: 'Twitter', link: 'https://twitter.com/evilfactorylabs' }], 15 | search: { 16 | enabled: false, 17 | indexName: '', 18 | algoliaAppId: process.env.GATSBY_ALGOLIA_APP_ID, 19 | algoliaSearchKey: process.env.GATSBY_ALGOLIA_SEARCH_KEY, 20 | algoliaAdminKey: process.env.ALGOLIA_ADMIN_KEY 21 | } 22 | }, 23 | sidebar: { 24 | forcedNavOrder: ['/chapter-1', '/chapter-2', '/chapter-3'], 25 | collapsedNav: [], 26 | links: [ 27 | { text: 'evilfactorylabs.org', link: 'https://evilfactorylabs.org' }, 28 | { 29 | text: 'Perkumpulan', 30 | link: 'https://evilfactorylabs.org' 31 | }, 32 | { text: 'Blog by evilfactorylabs', link: 'https://evilfactorylabs.org' }, 33 | { text: 'Brief by evilfactorylabs', link: 'https://evilfactorylabs.org' } 34 | ], 35 | frontline: false, 36 | ignoreIndex: true 37 | }, 38 | siteMetadata: { 39 | title: 'Belajar Rust', 40 | description: 'yes', 41 | ogImage: null, 42 | docsLocation: 43 | 'https://github.com/evilfactorylabs/handbook/tree/master/content', 44 | favicon: 'https://evilfactorylabs.org/evilfactory.png' 45 | }, 46 | pwa: { 47 | enabled: false, // disabling this will also remove the existing service worker. 48 | manifest: { 49 | name: 'Belajar Rust', 50 | short_name: 'Belajar Rust', 51 | start_url: '/', 52 | background_color: '#6b37bf', 53 | theme_color: '#6b37bf', 54 | display: 'standalone', 55 | crossOrigin: 'use-credentials', 56 | icons: [ 57 | { 58 | src: 'src/pwa-512.png', 59 | sizes: `512x512`, 60 | type: `image/png` 61 | } 62 | ] 63 | } 64 | } 65 | } 66 | 67 | module.exports = config 68 | -------------------------------------------------------------------------------- /content/chapter-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Chapter 1' 3 | metaTitle: 'Chapter 1 - Belajar Rust' 4 | metaDescription: 'Chapter 1 - Belajar Rust' 5 | --- 6 | 7 | Di **Chapter 1** ini adalah edisi _zero_, atau sebuah perkenalan awal sebelum memulai lebih jauh, seperti kata pepatah bahwa "Tak Kenal Maka Tak Sayang" untuk itu perlu untuk berkenalan lebih dulu. 8 | 9 | - [Sejarah](./chapter-1/1-sejarah) 10 | - [Developer Tools](./chapter-1/2-developer-tools) 11 | - [Getting Started](./chapter-1/3-getting-started) 12 | -------------------------------------------------------------------------------- /content/chapter-1/1-sejarah.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Sejarah' 3 | metaTitle: 'Sejarah' 4 | metaDescription: 'Sejarah' 5 | --- 6 | 7 | Rust merupakan bahasa pemrograman yang memiliki multi paradigma, bahasa ini mendukung beberapa macam paradigma contohnya paradigma fungsional, dan prosedural imperatif. secara sintaksis bahasa ini serupa dengan bahasa `C++` yang merupakan perkembangan dari bahasa `C` yang pertama kali dikembangkan pada tahun 1920-an. 8 | 9 | Sejarahnya Rust sejak pertamakali dikembangkan sebagai alternatif dari `C++` karena Mozila menemukan issue kelemahan yang dimiliki oleh bahasa `C++`, sehingga Rust dirancang dengan kemanan memori yang lebih baik. 10 | 11 | Sejak pertamakali dikembangkan di tahun 2014, hingga saat ini di tahun 2020 ada sekitar 2 ribu lebih kontributor di [_official_ repository github](https://github.com/rust-lang/rust), selain itu Rust juga menjadi salah satu bahasa yang ter favorit di survei stackoverflow. 12 | 13 | Rust tergolong dalam piranti lunak sumber terbuka, dengan lisensi [Apache License 2.0](https://id.wikipedia.org/wiki/Lisensi_Apache). 14 | 15 | Selain digunakan untuk [Servo](https://github.com/servo/servo) sebuah Layout browser Mozila, ada juga **Sistem Operasi** yang dikembangkan dengan menggunakan Rust yaitu [Redox](https://id.wikipedia.org/wiki/Redox_(sistem_operasi)). 16 | 17 | Lebih lanjut jika ingin melihat beberapa project terbaru yang dikembangkan dengan menggunakan Rust silahkan berkunjung ke https://crates.io/ yaitu direktori official untuk menyimpan beberbagai macam pustaka yang dibuat dengan menggunakan Rust. 18 | -------------------------------------------------------------------------------- /content/chapter-1/2-developer-tools.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Developer Tools' 3 | metaTitle: 'Developer Tools' 4 | metaDescription: 'Developer Tools' 5 | --- 6 | 7 | ## Developer Tools 8 | 9 | Sebagai perekayasa perangkat lunak tentunya tantangan terberatnya di kecepatan dan keamanan. Kecepatan berkaitan dengan waktu, bagaimana men deliver perangkat lunak dengan lebih cepat dan keaman berkaitan dengan kualitas yang dihasilkan, sehingga keduanya merupakan hal yang cukup penting untuk dipertimbangkan. 10 | 11 | Untuk itu Rust menawarkan beberapa dukungan tools yang mempermudah penggunanya untuk membuat sesuatu dengan baik. 12 | 13 | ## Cargo 14 | 15 | Cargo adalah paket manager yang sangat membantu untuk proses download paket library dari [https://crates.io/](https://crates.io/) dan juga berfungsi sebagai kompilator, dan juga membantu untuk proses distribusi aplikasi. 16 | 17 | ## Rustfmt 18 | 19 | Rustfmt adalah tool untuk format kode agar sesuai dengan gaya penulisan yang standar. Dengan bantuan `rustfmt` standarisasi kode akan semakin baik, keuntungan lainnya, `rustfmt` juga bisa diintegrasikan dengan proses CI/CD, sehingga proses formating menjadi lebih otomatis. 20 | 21 | ## Rust Compiler 22 | 23 | Rust compiler disingkat `rustc` adalah tool yang digunakan untuk mengkompilasi kode sumber menjadi [binary code](https://en.wikipedia.org/wiki/Binary_code) yaitu bahasa mesin yang dapat dijalankan secara langsung di perangkat sistem operasi. 24 | 25 | ## Rustup 26 | 27 | Kompilator Rust yang terinstall di komputer, atau sebuah toolchain management yang didesain untuk mengatur versi kompilator Rust _stable_, _beta_ dan _nightly_. Jika ingin melihat kemampuan `rustup` cukup ketikan perintah `rustup --help` di antarmuka terminal kalian. 28 | -------------------------------------------------------------------------------- /content/chapter-1/3-getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Getting Started' 3 | metaTitle: 'Getting Started' 4 | metaDescription: 'Getting Started' 5 | --- 6 | 7 | ## Installation 8 | 9 | Untuk menggunakan Rust, tentunya perlu untuk menginstalnya terlebih dahulu berikut adalah cara untuk meng-install Rust di mesin Sistem Operasi. Untuk pengguna **Linux** dan **Mac OS** cukup jalankan perintah ini pada terminal kalian `curl https://sh.rustup.rs -sSf | sh`, jika kalian pengguna sistem operasi **Windows** gunakan cmd sebagai pengganti terminal. 10 | 11 | Jika proses downloadnya selesai silahkan ketik perintah `rustc --version` untuk melihat versi compiler Rust yang terinstall. 12 | 13 | ```bash 14 | rustc --version 15 | rustc 1.41.0 (531a79984 2020-01-27) 16 | ``` 17 | 18 | ## Let's try to using Rust 19 | 20 | Untuk awal perkenalan baiknya mencoba untuk membuat program yang cukup sederhana, misalnya dengan mencetak sebuah teks “Halo Dunia” di antarmuka terminal. 21 | 22 | Catatan, sebelum lebih jauh penulis sangat merekomendasikan untuk menggunakan sistem operasi Linux atau Mac OS. 23 | 24 | Untuk tahapan awal silakan buat dahulu folder `belajar-rust` di komputer kalian dan kemudian buat file dengan nama `main.rs` didalam folder `belajar-rust` untuk pembuatannya silakan gunakan _text editor_ atau bisa langsung dari terminal dengan menggunakan perintah ini `mkdir belajar-rust;cd belajar-rust;touch main.rs` dan buka file `main.rs` dan isi dengan script dibawah ini. 25 | 26 | ```rust 27 | fn main() { 28 | println!("Halo Dunia"); 29 | } 30 | ``` 31 | 32 | Strukturnya akan tampil seperti gambar dibawah ini. 33 | 34 | ``` 35 | |-belajar-rust 36 | |--main.rs 37 | ``` 38 | 39 | Jika sudah, kemudian tahapan selanjutnya adalah proses eksekusi kode, agar kodenya bisa digunakan kita perlu untuk mengkompilasinya terlebih dahulu dengan menggunakan kompilator Rust yaitu `rustc`. 40 | 41 | ```bash 42 | cd belajar-rust 43 | rustc main.rs 44 | ./main 45 | Halo Dunia 46 | ``` 47 | 48 | Setelah menjalankan program “Halo Dunia” selanjutnya adalah melihat secara detail anatomi kode yang sudah kita tuliskan sebelumnya. 49 | 50 | ```rust 51 | // main.rs 52 | 53 | fn main() { 54 | // put your code here; 55 | } 56 | ``` 57 | 58 | Keyword `fn` mewakili `function` di Rust, dan `main()` adalah kode pertama yang akan dieksekusi ketika menjalankan kode program dengan menggunakan Rust. 59 | 60 | -------------------------------------------------------------------------------- /content/chapter-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Chapter 2' 3 | metaTitle: 'Chapter 2 - Belajar Rust' 4 | metaDescription: 'Chapter 2 - Belajar Rust' 5 | --- 6 | 7 | ## Chapter 2 8 | 9 | Di **Chapter 2** ini akan membahas sedikit beberapa hal dasar atau fundamental di Rust, seperti tipe data, perulangan dan konsep _ownership_ di Rust, dan beberapa hal _common_ lainnya. 10 | 11 | - [Variables & Mutability](./chapter-2/1-variables-and-mutability) 12 | - [Data Types](./chapter-2/2-data-types) 13 | - [Comments](./chapter-2/3-comments) 14 | - [Control Flow](./chapter-2/4-control-flow) 15 | - [Perulangan](./chapter-2/5-perulangan) 16 | - [Struct](./chapter-2/6-struct) 17 | - [Enum & Pattern Matching](./chapter-2/7-enum-and-pattern-matching) 18 | - [Ownership](./chapter-2/8-ownership) 19 | - [Slices](./chapter-2/9-slices) 20 | - [Lifetime](./chapter-2/10-lifetime) 21 | - [Common Collections](./chapter-2/11-common-collections) 22 | - [Error Handling](./chapter-2/12-error-handling) 23 | - [Generic Types & Traits](./chapter-2/13-generic-types-and-traits) 24 | -------------------------------------------------------------------------------- /content/chapter-2/1-variables-and-mutability.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Variables & Mutability' 3 | metaTitle: 'Variables & Mutability' 4 | metaDescription: 'Variables & Mutability' 5 | --- 6 | 7 | ## Variables & Mutability 8 | 9 | Secara _default_ variabel di Rust adalah _immutable_ tidak bisa diubah, untuk membuat sebuah variabel baru cukup menggunakan _keyword_ `let` diawal kemudian nama variabel dan nilainya, contohnya akan seperti ini `let name = "hello";` dan untuk merubah variabel yang _immutable_ menjadi _mutable_ cukup menambahkan _keyword_ `mut` setelah `let` jadinya akan seperti ini `let mut name = "hello";` berikut kode lengkapnya. 10 | 11 | ```rust 12 | fn main() { 13 | // immutable variable -> variable yang tidak bisa diubah 14 | let name = "Adiatma"; 15 | println!("{}", name); // "Adiatma" 16 | 17 | // mutable variable -> variable yang bisa diubah 18 | let mut change_name = "A"; 19 | println!("{}", change_name); // "A" 20 | change_name = "Adiatma"; 21 | println!("{}", change_name); // "Adiatma" 22 | } 23 | ``` 24 | 25 | ## Perbedaan Variables & Constants 26 | 27 | Secara default _constant_ itu memiliki sifat _immutable_, sehingga nilainya paten dan tidak bisa diubah. Untuk membuat _variable constant_ bisa dengan menggunakan _keyword_ `const`. 28 | 29 | ```rust 30 | const MAX_POINT: i32 = 1000; 31 | ``` 32 | 33 | _Constant_ sangatlah umum, dan bukan hal yang baru. _Constant_ merupakan nilai yang terikat dengan sebuah nama dan bersifat mutlak. Adapun yang membedakan antara variabel dengan _constant_ adalah sebagai berikut : 34 | - Umumnya _constant_ diawali dengan _keyword_ `const` sedangkan variabel `let`. 35 | - Variabel dapat menyimpan nilai dari _function_ atau hasil operasi perhitungan, sedangkan _constant_ hanya berupa nilai tetap. 36 | 37 | ## Shadowing 38 | 39 | _Shadow_ berbeda dengan konsep _mutable_, kegunaan fitur ini adalah agar kita bisa memiliki banyak nama variabel yang sama, dan _variable_ selanjutnya disebut sebagai _shadow_ atau bayangan dari variabel sebelumnya. Fitur ini berguna jika ingin menimpa nilai yang ada sebelumnya dengan proses selanjutnya. 40 | 41 | ```rust 42 | fn main() { 43 | let x = 1; 44 | let x = x * 2; 45 | 46 | println!("{}", x); // 2 47 | } 48 | ``` 49 | 50 | Contoh di atas terlihat _variabel_ `x` yang sebelumnya bernilai satu kemudian nilai `x` yang sebelumnya akan ditimpa dengan nilai baru. Jika disimpulkan perbedaan _shadow_ dengan _mutable variable_ adalah terletak di cara pakainya. 51 | 52 | _Shadow_ akan menimpa nilai yang lama dengan yang baru, _mutable variable_ untuk menggantikan nilai yang sebelumnya dengan yang baru. Untuk penggunaannya _shadow_ butuh untuk dikombinasikan dengan `let` sehingga berbeda jika dibandingkan dengan _mutable variable_ yang menimpa nilai baru tanpa menggunakan `let`. 53 | -------------------------------------------------------------------------------- /content/chapter-2/10-lifetime.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Lifetime' 3 | metaTitle: 'Lifetime' 4 | metaDescription: 'Lifetime' 5 | --- 6 | 7 | ## Lifetime 8 | 9 | _Lifetime_ adalah bagian dari fitur _ownership_ yang tersedia di Rust. Sebelumnya jika kalian melihat bahasan tentang _borrowing_ & _references_, dimana _references_ akan tetap valid jika berada didalam ruang lingkupnya, namun jika telah berada diluar _scope_ maka semuanya akan terhapus dari memori. Nah bisa dibilang _lifetime_ adalah _scope_, fitur ini memudahkan kita untuk mengatur _scope_ yang ada di Rust. 10 | 11 | Jika bermain dengan _references_ ada hal yang menjadi _problem_ yaitu _dangling references_, masalah ini dikenal dengan referensi yang menggantung, ini terjadi karena ada sebuah nilai yang mereferensi ke sebuah alamat yang tidak valid, di dalam memori. Untuk menangani masalah tersebut Rust menyediakan fitur _lifetime_ yang bisa kita gunakan untuk mengatur ruang lingkup sehingga bisa tetap valid. 12 | 13 | ## Dangling References 14 | 15 | ```rust 16 | fn main() { 17 | let s; 18 | 19 | { 20 | let x = 5; 21 | s = &x 22 | } 23 | println!("{}", s); 24 | } 25 | ``` 26 | 27 | ```bash 28 | error[E0597]: `x` does not live long enough 29 | --> main.rs:6:13 30 | | 31 | 6 | s = &x 32 | | ^^ borrowed value does not live long enough 33 | 7 | } 34 | | - `x` dropped here while still borrowed 35 | 8 | println!("{}", s); 36 | | - borrow later used here 37 | ``` 38 | 39 | 40 | Kode diatas punya dua ruang lingkup yaitu _global scope_, dan _block scope_, _variable_ `s` memiliki masa hidup yang lebih lama jika dibandingkan dengan _variabel_ `x`, kemudian nilai dari _variabel_ `s` di timpa dengan _variable references_ yang mereferensi ke `x`, atau dipinjamkan nilai oleh `x`. Nah masalahnya adalah _variable_ `s` kemudian diakses setelah _variable_ `x` terhapus dari memori, sehingga tidak valid lagi nilainya, ini yang dinamakan _dangling references_. 41 | 42 | 43 | ## Lifetime in function 44 | 45 | _Lifetime_ dapat dibuat dengan menggunakan _keyword_ `'a` dimana `a` mewakili _lifetime_. Jika di implementasi di dalam sebuah _function_ akan seperti ini. 46 | 47 | ```rust 48 | fn main() { 49 | let first_letter = "Hallo indonesia"; 50 | let second_letter = "hallo"; 51 | 52 | let longest = longest(first_letter, second_letter); 53 | 54 | println!("{}", longest); // "Hallo indonesia" 55 | } 56 | 57 | fn longest<'a>(first: &'a str, second: &'a str) -> &'a str { 58 | if first.len() > second.len() { 59 | first 60 | } else { 61 | second 62 | } 63 | } 64 | ``` 65 | 66 | Terlihat di atas _lifetime_ `'a` mewakili ruang lingkup yang ada di dalam _function longest_ sehingga _scope_ dari dua parameter di atas akan tetap _valid_. 67 | -------------------------------------------------------------------------------- /content/chapter-2/11-common-collections.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Common Collections' 3 | metaTitle: 'Common Collections' 4 | metaDescription: 'Common Collections' 5 | --- 6 | 7 | ## Collections 8 | 9 | Rust punya tipe data _collections_, yang merupakan kumpulan atau koleksi data dalam berbagai tipe dan nilai. Ada beberapa tipe data _collections_ yang cukup populer di Rust. Berikut adalah beberapa daftarnya. 10 | 11 | - _Vector_ mirip seperti _array_ tetapi berbeda. 12 | - _Strings_ digunakan untuk menyimpan tipe data _character_. 13 | - _Hash Map_ memungkinkan kita untuk menyimpan nilai yang terasosiasi dengan _key_ tertentu. 14 | 15 | ## Vector 16 | 17 | `Vec` yang dikenal sebagai _vector_, sebuah tipe data yang dapat menyimpan lebih dari satu nilai kedalam _single_ data struktur yang nilainya dapat ditambahkan atau dirubah. 18 | 19 | Untuk menginisiasi _vector_ baru, cukup dituliskan seperti ini `Vec::new()`. 20 | 21 | 22 | ```rust 23 | fn main() { 24 | let numbers: Vec = Vec::new(); 25 | } 26 | ``` 27 | 28 | Vector juga dapat diinisiasi dengan standar nilai _default_ caranya dengan menggunakan _syntax_ seperti ini `vec![]` berikut adalah contohnya. 29 | 30 | 31 | ```rust 32 | fn main() { 33 | let numbers: Vec = vec![1, 2, 3]; 34 | } 35 | ``` 36 | 37 | 38 | Untuk meng-_update_ dan menambahkan nilai baru, _vector_ punya _method_ bawaan yaitu `push`. 39 | 40 | 41 | ```rust 42 | fn main() { 43 | let mut numbers: Vec = Vec::new(); 44 | numbers.push(1); 45 | } 46 | ``` 47 | 48 | Untuk bisa menggunakan `push` tambahkan _keyword_ `mut` setelah _keyword_ `let` tujuannya untuk membuatnya menjadi _mutable_. 49 | 50 | Cara membaca elemen nilai yang berada didalam sebuah _vector_, bisa dengan menggunakan method `get` seperti ini `numbers.get(index)` berikut adalah contoh detailnya. 51 | 52 | 53 | ```rust 54 | fn main() { 55 | let mut numbers: Vec = Vec::new(); 56 | numbers.push(1); 57 | match numbers.get(0) { 58 | Some(v) => println!("{}", v), 59 | None => println!("index not found!"), 60 | } // 1 61 | } 62 | ``` 63 | 64 | Atau bisa juga dengan cara yang seperti ini, dengan menggunakan _references_. 65 | 66 | ```rust 67 | fn main() { 68 | let mut numbers: Vec = Vec::new(); 69 | numbers.push(1); 70 | 71 | println!("{}", &numbers[0]); // 1 72 | } 73 | ``` 74 | 75 | Jika ingin digunakan dengan cara di _loop_ bisa juga jadinya seperti ini. 76 | 77 | ```rust 78 | fn main() { 79 | let mut numbers: Vec = Vec::new(); 80 | numbers.push(1); 81 | 82 | for number in numbers { 83 | println!("{}", number); 84 | } // 1 85 | } 86 | ``` 87 | 88 | Selain itu bisa juga dengan menggunakan _mutable references_. 89 | 90 | ```rust 91 | fn main() { 92 | let mut numbers: Vec = Vec::new(); 93 | numbers.push(1); 94 | 95 | for number in &mut numbers { 96 | *number += 10; 97 | println!("{}", number); // output -> 11 98 | } 99 | } 100 | ``` 101 | 102 | ## String 103 | 104 | Kita dapat menyimpan text _UTF-8 encoded_ ke dalam tipe data _String_. Rust hanya punya satu tipe _string_ yang tersedia di inti bahasanya, yakni `str` yang dikenal dengan _string slice_ yang biasanya terlihat seperti ini `&str` istilahnya _borrowed string_. 105 | 106 | Rust juga memiliki tipe data `String` untuk kemudahan pengolahan data _mutable_ dengan menggunakan _string_. Sehingga bisa menyimpan _encode_ yang berbeda-beda dengan mudah. 107 | 108 | Untuk penggunaanya sedikit mirip dengan apa yang bisa dilakukan oleh _vector_. Untuk inisiasi cukup dengan cara seperti ini `String::new()` dan untuk konversi tipe data _string literal_ menjadi `String` bisa dengan menggunakan _method_ `to_string()`, seperti contoh dibawah ini. 109 | 110 | ```rust 111 | fn main() { 112 | let text = "Hello World"; 113 | let convert_text_to_string = text.to_string(); 114 | println!("{}", convert_text_to_string); // "Hello World" 115 | } 116 | ``` 117 | 118 | Selain itu bisa juga membuat _string_ dengan nilai yang bawaan atau istilahnya _initial content_ contohnya seperti ini. 119 | 120 | ```rust 121 | fn main() { 122 | let hello = String::from("Hello"); 123 | println!("{}", hello); 124 | } 125 | ``` 126 | 127 | Untuk merubah data atau memperbaharui bisa dengan menambahkan _keyword_ `mut` setelah `let` seperti contoh berikut. 128 | 129 | ```rust 130 | fn main() { 131 | let mut hello = String::from("Hello"); 132 | hello.push_str(" World"); 133 | println!("{}", hello); // "Hello World" 134 | } 135 | ``` 136 | 137 | Kemudian juga bisa menggabungkan data dengan menggunakan _function_ `format!` seperti ini. 138 | 139 | ```rust 140 | fn main() { 141 | let hello = String::from("Hello"); 142 | let world = String::from("World"); 143 | let hello_world = format!("{} {}", hello, world); 144 | println!("{}", hello_world); "Hello World" 145 | } 146 | ``` 147 | 148 | ## HashMap 149 | 150 | Untuk menyimpan koleksi data dengan _key_ yang terasosiasi bisa menggunakan _HashMap_. Syntaxnya seperti ini `HashMap<K, V>` yaitu K mewakili _key_ dan V adalah _value_. 151 | 152 | Untuk menggunakan _HashMap_ kita butuh untuk mengambilnya dari _standard library_, bisa dengan menggunakan `use` seperti contoh dibawah. 153 | 154 | ```rust 155 | use std::collections::HashMap; 156 | 157 | fn main() { 158 | let mut scores = HashMap::new(); 159 | 160 | scores.insert(String::from("Team A"), 10); 161 | scores.insert(String::from("Team B"), 11); 162 | 163 | println!("{:?}", scores); // {"Team B": 11, "Team A": 10} 164 | } 165 | ``` 166 | 167 | Cara mengakses _value_ pada _HashMap_ bisa menggunakan _method_ `get` dan _key_ dimasukan sebagai parameter, lengkapnya seperti dibawah ini. 168 | 169 | ```rust 170 | use std::collections::HashMap; 171 | 172 | fn main() { 173 | let mut scores = HashMap::new(); 174 | 175 | scores.insert(String::from("Team_A"), 10); 176 | scores.insert(String::from("Team_B"), 11); 177 | 178 | println!("{:?}", scores.get(&String::from("Team_A"))); // Some(10) 179 | } 180 | ``` 181 | 182 | Kemudian tahapan selanjutnya dari _HashMap_ adalah cara untuk memperbaharui nilai jika _key_ tersebut tidak memiliki nilai. 183 | 184 | ```rust 185 | use std::collections::HashMap; 186 | 187 | fn main() { 188 | let mut scores = HashMap::new(); 189 | 190 | scores.insert(String::from("Team_A"), 10); 191 | 192 | scores.entry(String::from("Team_A")).or_insert(11); 193 | scores.entry(String::from("Team_B")).or_insert(12); 194 | 195 | println!("{:?}", scores); 196 | } 197 | ``` 198 | 199 | Berikut adalah keluarannya. 200 | 201 | ```bash 202 | {"Team_A": 10, "Team_B": 12} 203 | ``` 204 | 205 | Terlihat _key_ `Team_A` tetap menggunakan nilai yang sebelumnya, dan _key_ `Team_B` ditambahkan nilai baru. 206 | 207 | Selanjutnya kita akan coba membuat sebuah text yang kemudian akan dipecah berdasarkan _white space_ lalu setiap kata yang sama akan disimpan di dalam _HashMap_ dan dihitung jumlahnya. 208 | 209 | ```rust 210 | use std::collections::HashMap; 211 | 212 | fn main() { 213 | let text = "hari ini saya ngantuk saya pengen tidur karena ngantuk"; 214 | 215 | // please find a couple text and count it. 216 | let mut result = HashMap::new(); 217 | 218 | for t in text.split_whitespace() { 219 | let count = result.entry(t).or_insert(0); 220 | *count += 1; 221 | } 222 | 223 | println!("{:?}", result); 224 | } 225 | ``` 226 | 227 | Berikut adalah keluarannya. 228 | 229 | ```bash 230 | {"karena": 1, "hari": 1, "saya": 2, "tidur": 1, "ini": 1, "pengen": 1, "ngantuk": 2} 231 | ``` 232 | 233 | Sebelumnya terdapat sebuah _variable_ `text` yang diisi dengan tipe data _string_. Nah, kemudian ada _mutable variable_ dengan nama _result_ yang tujuannya sih untuk menampung data _string_ nya, di tahap selanjutnya kita akan memotong text berdasarkan _white space_ dengan menggunakan function `split_whitespace()` yaitu fungsi bawaan di Rust untuk memotong berdasarkan _whitespace_ dan kemudian di dalam perulangan tersebut kita _define_ lagi _variable_ `count` yang isinya untuk mengupdate _HashMap_ _key_ berdasarkan text elemen, kemudian ada function `or_insert()` tujuannya untuk mengambil _value_ berdasarkan _index_, yang _default_ nilainya adalah 0, kemudian kita menggunakan tanda bintang `(*)` agar bisa mengakses nilai dari _count_ secara langsung, nah hal ini disebut dengan _dereferences_. 234 | -------------------------------------------------------------------------------- /content/chapter-2/12-error-handling.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Error Handling' 3 | metaTitle: 'Error Handling' 4 | metaDescription: 'Error Handling' 5 | --- 6 | 7 | ## Error Handling 8 | 9 | Faktanya _error_ adalah hal yang paling sering kita jumpai dalam proses _development_. Rust berkomitmen untuk membuat _error_ dengan baik dan mudah dibaca, sehingga ketika sesuatu berjalan tidak sesuai ekspektasi dapat diketahui dengan cepat. Untuk itu Rust mengelompokan _error_ dalam dua kategori, yaitu _recoverable_ and _unrecoverable_. 10 | 11 | ## Unrecoverable Errors dengan menggunakan panic! 12 | 13 | Sebuah _error_ yang tidak terjangkau atau tidak _coverable_. Untuk kasus ini Rust punya fungsi bawaan yaitu _panic!_ yang tujuannya untuk mencetak pesan _error_. 14 | 15 | Standarnya ketika terjadi _error_ `panic!` Rust akan kembali ke _stack_ dan menghapus data dari _function_ tersebut. 16 | 17 | ```rust 18 | fn main() { 19 | panic!("panic!!!"); 20 | } 21 | ``` 22 | 23 | ```bash 24 | Compiling rust-by-example v0.1.0 (/Users/adiatma/Work/rust-by-example) 25 | Finished dev [unoptimized + debuginfo] target(s) in 0.30s 26 | Running `target/debug/rust-by-example` 27 | thread 'main' panicked at 'panic!!!', src/main.rs:2:5 28 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. 29 | ``` 30 | 31 | ## Recoverable Errors dengan menggunakan Result 32 | 33 | Berbeda dengan _error_ sebelumnya, Rust juga memiliki pesan _error_ yang _recoverable_ sehingga kita bisa buat pemetaan _error_-nya dengan baik. 34 | 35 | Berikut adalah cara untuk membuat _error_ `result` di Rust. 36 | 37 | ```rust 38 | enum Result { 39 | Ok(T), 40 | Err(E) 41 | } 42 | ``` 43 | 44 | `T` dan `E` adalah _generic type_ parameter, `T` itu merepresentasikan tipe data, dan `E` merepresentasikan _error_. 45 | 46 | Selanjutnya kita akan coba buat program sederhana, dengan menggunakan _file system_ untuk membuka file `hello.txt` di dalam direktori, jika file tersebut tidak ditemukan maka kita akan menampilkan pesan error. 47 | 48 | ```rust 49 | use std::fs::File; 50 | fn main() { 51 | let file = File::open("hello.txt"); 52 | Let file = match file { 53 | Ok(file) => file, 54 | Err(error) => { 55 | panic!("Problem: {:?}", error); 56 | }, 57 | }; 58 | } 59 | ``` 60 | 61 | ```bash 62 | warning: unused variable: `file` 63 | --> src/main.rs:5:9 64 | | 65 | 5 | let file = match file { 66 | | ^^^^ help: consider prefixing with an underscore: `_file` 67 | | 68 | = note: `#[warn(unused_variables)]` on by default 69 | 70 | Finished dev [unoptimized + debuginfo] target(s) in 0.24s 71 | Running `target/debug/rust-by-example` 72 | thread 'main' panicked at 'Problem: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:8:13 73 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. 74 | ``` 75 | 76 | Selanjutnya kita akan coba untuk _refactor_ kode sebelumnya dengan mengecek tipe _error_. 77 | 78 | ```rust 79 | use std::fs::File; 80 | use std::io::ErrorKind; 81 | 82 | fn main() { 83 | File::open("hello.txt").unwrap_or_else(|error| { 84 | if error.kind() == ErrorKind::NotFound { 85 | File::create("hello.txt").unwrap_or_else(|error| { 86 | panic!("{:?}", error); 87 | }) 88 | } else { 89 | panic!("{:?}", error); 90 | } 91 | }); 92 | } 93 | ``` 94 | 95 | Pada kode sebelumnya menggunakan `match`, nah setelah di _refactor_, _keyword_ `match` tadi sudah tidak terlihat, karena sudah digantikan oleh _function_ `unwrap_or_else()` yang kemudian menggunakan _closure_ untuk melihat _error_ lebih lanjut, dan jika file `hello.txt` tidak ditemukan maka file baru akan dibuat. 96 | 97 | Selain `unwrap_or_else()` kalian juga bisa menggunakan `unwrap()` saja atau digantikan dengan `expect()`. 98 | 99 | ```rust 100 | use std::fs::File; 101 | 102 | fn main() { 103 | File::open("hello.txt").expect("File not found!"); 104 | } 105 | ``` 106 | 107 | ```bash 108 | thread 'main' panicked at 'File not found!: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/libcore/result.rs:1188:5 109 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. 110 | ``` 111 | -------------------------------------------------------------------------------- /content/chapter-2/13-generic-types-and-traits.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Generic Types & Traits' 3 | metaTitle: 'Generic Types & Traits' 4 | metaDescription: 'Generic Types & Traits' 5 | --- 6 | 7 | ## Generic Types & Traits 8 | 9 | Generic adalah cara untuk membuat tipe data menjadi lebih _flexible_, sehingga mudah untuk dipakai, berulang-ulang dan terhindar dari masalah _duplicate_. 10 | 11 | Menghapus duplikasi dengan mengekstraknya menjadi sebuah _function_ 12 | 13 | Sebelum jauh membahas tentang _generic type_, ada baiknya untuk melihat sebuah studi kasus sederhana yaitu mengurangi _duplication_ dengan merubahnya menjadi _function_ yang dapat dipakai berulang-ulang. 14 | 15 | Kita akan coba membuat sebuah program sederhana untuk mencari angka terbesar dari sebuah koleksi data _number_, berikut adalah contoh kodenya. 16 | 17 | ```rust 18 | fn main() { 19 | let numbers = vec![1, 2, 3, 4, 5]; 20 | 21 | let mut largest = numbers[0]; 22 | 23 | for number in numbers { 24 | if number > largest { 25 | largest = number 26 | } 27 | } 28 | 29 | println!("{}", largest); // 5 30 | } 31 | ``` 32 | 33 | _Variable_ _numbers_ di atas adalah tipe data _vector_ yang menampung daftar angka dari 1 sampai 5, kemudian setelahnya ada _mutable variable_ yang akan mengambil isi _index_ pertama dari _variable numbers_, kemudian kita coba mengambil semua _item_ yang ada di _variable numbers_ dengan menggunakan _loop_ dan didalam perulangan tersebut kita menggunakan `if` _statement_ untuk mengecek _item number_, jika lebih besar dari _mutable variable largest_ maka akan dimasukan sebagai nilai baru di _variable largest_. 34 | 35 | Pertanyaannya bagaimana jika kita ingin menggunakan fungsi yang sama di tempat yang berbeda, tentunya hal yang paling mudah adalah mengekstrak fungsi tadi kedalam sebuah fungsi. 36 | 37 | ```rust 38 | fn largest(list: &[i32]) -> i32 { 39 | let mut largest = list[0]; 40 | 41 | for &number in list { 42 | if number > largest { 43 | largest = number 44 | } 45 | } 46 | 47 | largest 48 | } 49 | 50 | fn main() { 51 | let numbers = vec![1, 2, 3, 4, 5]; 52 | 53 | let result = largest(&numbers); 54 | 55 | println!("{}", result); 56 | } 57 | ``` 58 | 59 | Setelah refactor dengan mengekstrak sebuah operasi pencarian angka terbesar didalam sebuah koleksi data _number_, sekarang fungsi _largest_ menjadi sangat flexible dan dapat dipakai berkali - kali tanpa perlu menulis ulang kodenya. 60 | 61 | Fungsi _largest_ memiliki sebuah parameter yang mana merepresentasikan nilai konkret dari slice `i32` kedalam sebuah _function_. 62 | 63 | Pertanyaan yang terjadi selanjutnya adalah bagaimana jika kita ingin menggunakan fungsi tersebut untuk tipe data yang berbeda, nah untuk menjawab hal tersebut perlu untuk memahami _generic types_. 64 | 65 | ## Generic Data Types 66 | 67 | Masih terkait studi kasus sebelumnya dimana ada sebuah _function_ yang bisa mengevaluasi sebuah _integer_ dan kemudian mengembalikan angka yang paling besar yang terdapat pada sebuah koleksi data. 68 | 69 | Misalnya fungsi tadi kita ingin bisa digunakan untuk tipe data lainnya, yaitu untuk evaluasi sebuah tipe data _string_. 70 | 71 | Dengan menggunakan _generic type_ hal tersebut memungkinkan, untuk menggunakan tipe data generic didalam sebuah _function_ cukup dengan merubah fungsi sebelumnya menjadi seperti ini `fn largest(list: &[T]) -> T {}` sehingga pada penggunaannya dapat disesuaikan tipe datanya. 72 | 73 | ```rust 74 | fn largest(list: &[T]) -> T { 75 | let mut largest = list[0]; 76 | 77 | for &item in list { 78 | if item > largest { 79 | largest = item 80 | } 81 | } 82 | 83 | largest 84 | } 85 | 86 | fn main() { 87 | let numbers = vec![10, 1, 2]; 88 | let largest_number = largest(&numbers); 89 | println!("{}", largest_number); 90 | 91 | let words = vec!["hello", "adiatma"]; 92 | let largest_words = largest(&words); 93 | println("{}", largest_words); 94 | } 95 | ``` 96 | 97 | ```bash 98 | Compiling rust-by-example v0.1.0 (/Users/adiatma/Work/rust-by-example) 99 | error[E0423]: expected function, found macro `println` 100 | --> src/main.rs:20:5 101 | | 102 | 20 | println("{}", largest_words); 103 | | ^^^^^^^ help: use `!` to invoke the macro: `println!` 104 | 105 | error[E0369]: binary operation `>` cannot be applied to type `T` 106 | --> src/main.rs:5:17 107 | | 108 | 5 | if item > largest { 109 | | ---- ^ ------- T 110 | | | 111 | | T 112 | | 113 | = note: `T` might need a bound for `std::cmp::PartialOrd` 114 | 115 | error: aborting due to 2 previous errors 116 | 117 | Some errors have detailed explanations: E0369, E0423. 118 | For more information about an error, try `rustc --explain E0369`. 119 | error: could not compile `rust-by-example` 120 | ``` 121 | 122 | Loh, kok error? 123 | 124 | Pada _capture_ error diatas yang di _mention_ adalah `std::cmp::PartialOrd` yang bisa disebut _trait_. 125 | 126 | Saat ini error state yang terjadi diatas adalah karena fungsi _largest_ tidak bekerja untuk setiap kemungkinan `T` type, karena kita ingin membandingkan tipe data `T` yang terdapat didalam _function_. Untuk perbandingan kita perlu untuk menggunakan `std::cmp::PartialOrd`. 127 | 128 | ## Generic Type in Struct 129 | 130 | Tipe umum atau _generic type_ bisa juga digunakan di _struct_, gambarnya seperti contoh dibawah. 131 | 132 | ```rust 133 | struct Point { 134 | x: T, 135 | y: T 136 | } 137 | 138 | fn main() { 139 | let integer = Point { x: 1, y: 2 }; 140 | let float = Point { x: 1.2, y: 2.1 }; 141 | 142 | println!("{} {}", integer.x, float.y); // 1 2.1 143 | } 144 | ``` 145 | 146 | ## In enum definition 147 | 148 | Selain bisa digunakan di `struct`, tipe data _generic_ juga bisa digunakan di _enum_. 149 | 150 | ```rust 151 | enum Result { 152 | Ok(T), 153 | Err(E), 154 | } 155 | ``` 156 | 157 | ## In Method Definition 158 | 159 | ```rust 160 | struct Point { 161 | x: T, 162 | y: T 163 | } 164 | 165 | impl Point { 166 | fn print_x(&self) -> &T { 167 | &self.x 168 | } 169 | } 170 | 171 | fn main() { 172 | let integer = Point { x: 1, y: 2 }; 173 | let float = Point { x: 1.2, y: 2.1 }; 174 | 175 | println!("{} {} {}", integer.x, float.y, integer.print_x()); // 1 2.1 1 176 | } 177 | ``` 178 | 179 | ## Multi Generic Types with Method 180 | 181 | ```rust 182 | struct Point { 183 | x: T, 184 | y: U 185 | } 186 | 187 | impl Point { 188 | fn mixup(self, other: Point) -> Point { 189 | Point { 190 | x: self.x, 191 | y: other.y 192 | } 193 | } 194 | } 195 | 196 | fn main() { 197 | let p1 = Point { x: 1, y: 2 }; 198 | let p2 = Point { x: "Hello", y: "h" }; 199 | 200 | let p3 = p1.mixup(p2); 201 | 202 | println!("{} {}", p3.x, p3.y); // 1 h 203 | } 204 | ``` 205 | 206 | ## Traits 207 | 208 | Sebuah _trait_ dapat meminta kepada kompilator Rust untuk bagian dari _functionality type_ dapat di _share_ dengan tipe data lainnya. Kita bisa menggunakan _trait_ untuk _share behaviour_ dengan cara yang abstrak. Juga kita bisa menggunakan _trait_ untuk mengikat spesifikasi dari _generic type _yang bisa untuk setiap tipe data. 209 | 210 | _Trait_ mirip seperti sebuah _interface_ pada bahasa pemrograman lainnya. Seperti sebuah _interface_ pada sebuah _object_, sebuah _trait_ dapat dibagikan ke beberapa _method_ sehingga punya referensi tipe yang sama. Berikut adalah contoh penggunaan _trait_ pada sebuah _method_. 211 | 212 | ```rust 213 | trait Summary { 214 | fn summarize(&self) -> String; 215 | } 216 | 217 | struct Article { 218 | title: String, 219 | author: String 220 | } 221 | 222 | impl Summary for Article { 223 | fn summarize(&self) -> String { 224 | format!("{} author of {}", self.author, self.title) 225 | } 226 | } 227 | 228 | fn main() { 229 | let first_article = Article { 230 | title: String::from("Buku Rust Bahasa Indonesia"), 231 | author: String::from("Adiatma") 232 | }; 233 | 234 | println!("{}", first_article.summarize()); // Adiatma author of Buku Rust Bahasa Indonesia 235 | } 236 | ``` 237 | -------------------------------------------------------------------------------- /content/chapter-2/2-data-types.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Data Types' 3 | metaTitle: 'Data Types' 4 | metaDescription: 'Data Types' 5 | --- 6 | 7 | ## Data Types 8 | 9 | Setiap nilai di _Rust_ memiliki tipe data tertentu, Rust punya dua _subset_ tipe data yaitu **_scalar_** dan **_compound_**. 10 | 11 | ## Scalar 12 | 13 | _Scalar_ merepresentasikan nilai tunggal, ada empat tipe data _scalar_ yaitu _integer_, _floating-point_, _numeric operations_, _booleans_, dan _char_. 14 | 15 | ### Integer 16 | 17 | Tipe data _integer_ dibagi menjadi dua yaitu **_signed_** dan **_unsigned_**. _Signed integer_ diidentifikasi dengan _keyword_ `i` dan _unsigned integer_ ditandai dengan `u`. berikut adalah tabelnya. 18 | 19 | 20 | 21 | 23 | 25 | 27 | 28 | 29 | 31 | 33 | 35 | 36 | 37 | 39 | 41 | 43 | 44 | 45 | 47 | 49 | 51 | 52 | 53 | 55 | 57 | 59 | 60 | 61 | 63 | 65 | 67 | 68 | 69 | 71 | 73 | 75 | 76 |
Length 22 | Signed 24 | Unsigned 26 |
8-bit 30 | i8 32 | u8 34 |
16-bit 38 | i16 40 | u16 42 |
32-bit 46 | i32 48 | u32 50 |
64-bit 54 | i64 56 | u64 58 |
128-bit 62 | i128 64 | u128 66 |
arch 70 | isize 72 | usize 74 |
77 | 78 | Perbedaan _Signed_ dan _Unsigned_, _Signed_ dapat menyimpan number yang positif dan negatif, _Unsigned_ hanya menyimpan nilai yang positif. 79 | 80 | ### Floating Point 81 | 82 | Rust punya tipe data primitif yaitu _floating point numbers_, tipe data ini ditandai dengan `f32` dan `f64` yang mana adalah `32 bit` dan `64 bit`. 83 | 84 | ```rust 85 | fn main() { 86 | let x = 2.0; // f64 default 87 | let c: f32 = 3.2; // f32 88 | } 89 | ``` 90 | 91 | ### Numeric Operations 92 | 93 | Rust juga mendukung operasi matematika, misalnya untuk operasi penjumlahan, pembagian, dan pengurangan. 94 | 95 | ```rust 96 | fn main() { 97 | // addition 98 | let addition = 5 + 10; 99 | // subtraction 100 | let subtraction = 95.5 - 4.3; 101 | // multiplication 102 | let multiplication = 4 * 30; 103 | // division 104 | let division = 56.7 / 32.2; 105 | // modulo 106 | let modulo = 43 % 5; 107 | } 108 | ``` 109 | 110 | ### Booleans 111 | 112 | Mirip seperti tipe data pada umumnya, tipe data ini yaitu _boolean_ memiliki dua nilai yaitu `true` dan `false`. 113 | 114 | ### Char 115 | 116 | _Char_ adalah salah satu tipe data primitif yang tersedia di Rust. Tipe data ini merepresentasikan _ASCII (Accented letters)_. 117 | 118 | ```rust 119 | let name = "Adiatma"; 120 | ``` 121 | 122 | ## Compound 123 | 124 | Tipe data _compound_ dapat mengoleksi atau mengumpulkan nilai-nilai ke dalam satu tipe data, ada dua tipe data _compound_ primitif yang diketahui oleh Rust yaitu _tuples_ dan _array_. 125 | 126 | ### Tuple 127 | 128 | _Tuple_ adalah cara untuk mengelompokkan tipe data tertentu ke dalam satu bentuk kelompok seperti dibawah ini. 129 | 130 | ```rust 131 | let tup: (i32, f32, u8) = (500, 6.4, 1); 132 | ``` 133 | Seperti contoh di atas, setiap posisi pada _tuple_ memiliki tipe data yang beragam. Artinya, setiap nilai di dalam _tuple_ tidak harus sama. Untuk mengakses nilai tertentu di dalam sebuah _tuple_, dapat dilakukan dengan cara berikut: 134 | 135 | ```rust 136 | fn main() { 137 | let tup: (i32, f32, u8) = (500, 6.4, 1); 138 | let (a, b, c) = tup; 139 | println!("Nilai b yaitu {}", b); 140 | } 141 | ``` 142 | Selain cara di atas, mengakses nilai di dalam _tuple_ dapat dilakukan secara langsung dengan tanda `.` seperti berikut ini: 143 | 144 | ```rust 145 | fn main() { 146 | let tup: (i32, f32, u8) = (500, 6.4, 1); 147 | let second = tup.1; 148 | println!("Nilai b yaitu {}", second); 149 | } 150 | ``` 151 | _Tuple_ memiliki ukuran tetap dan tidak dapat berubah dan hanya dideklarasi sekali saja. 152 | 153 | ### Array 154 | 155 | _Array_ adalah tipe data koleksi namun berbeda dengan _tuple_, _array_ di Rust sedikit berbeda dengan _array_ pada bahasa program umumnya, tipe data _array_ di Rust punya ukuran yang _absolute_ atau pasti, contohnya seperti dibawah. 156 | 157 | 158 | ```rust 159 | let numbers: [i32; 2] = [1, 2]; 160 | ``` 161 | Cara mengakses nilai pada suatu _array_ dapat dilakukan dengan cara berikut: 162 | 163 | ```rust 164 | let numbers = [1, 2, 3, 4, 5]; 165 | let second = numbers[1]; 166 | println!("Nilai kedua yaitu {}", second); 167 | ``` 168 | 169 | Penggunaan _array_ sangat bermanfaat jika kita ingin menggunakan alokasi data di _stack_ memori. Sama seperti _tuple_, tipe data _array_ memiliki ukuran tetap, namun _array_ tidak dapat menyimpan lebih dari 1 tipe data. 170 | 171 | ### Function 172 | 173 | Untuk membuat sebuah _function_ baru cukup diawali dengan _keyword_ `fn` yang kemudian depannya nama _function_ contohnya seperti dibawah ini. 174 | 175 | ```rust 176 | fn hello() { 177 | // statements or expressions here; 178 | } 179 | ``` 180 | 181 | Kemudian _conventional style_ untuk penamaan _function_ menggunakan _snake case_. 182 | 183 | ### Pengelompokan tipe function 184 | 185 | Rust sengaja mengelompokan _function_ berdasarkan fungsi dan kegunaannya. Sehingga mudah untuk dikenali dan dibedakan. Berikut adalah beberapa kelompok _function_ di Rust. 186 | 187 | 188 | ### Normal Function 189 | 190 | Adalah fungsi biasa yang tidak memiliki parameter dan tidak mengembalikan nilai, bisa dibilang ini _naked function_ atau fungsi yang telanjang. 191 | 192 | 193 | ### Function Parameters 194 | 195 | Fungsi dengan sebuah parameter nilai yang disisipkan kedalam _function_ untuk diolah lebih lanjut. contohnya seperti ini. 196 | 197 | ```rust 198 | fn hello(name: &str) { 199 | println!("{}", name); 200 | } 201 | ``` 202 | 203 | ### Function Bodies Contain Statements & Expressions 204 | 205 | Rust adalah _expression-based language_, mungkin di bahasa program yang lain hal ini tidak menjadi sebuah perbedaan, di Rust berbeda, untuk melihat perbedaannya kita coba untuk pahami dulu apa itu _statements_ dan _expression_. 206 | 207 | **_Statements_** adalah sebuah instruksi untuk melakukan aksi dan tidak mengembalikan sebuah nilai. **_Expressions_** mengevaluasi dan mengembalikan sebuah nilai. 208 | 209 | Contoh _statement_ `let x = 1;` ini adalah _statement_, dan _expression_ `{ let x = 3; x + 1 }`. **_Statement_** biasanya ditandai dengan _semicolon_ (`;`) di akhir _statement_, dan _expression_ tidak memiliki _semicolon_. 210 | 211 | 212 | ### Function with Return Values 213 | 214 | Fungsi yang bisa mengembalikan sebuah nilai, fungsi ini ditandai dengan _keyword_ `->` setelah nama fungsi, contohnya seperti dibawah ini. 215 | 216 | ```rust 217 | fn add(x: i32, y: i32) -> i32 { 218 | x + y 219 | } 220 | ``` 221 | 222 | Nah contoh diatas adalah fungsi sederhana untuk menjumlahkan x dan y, yang kembaliannya adalah tipe data _signed integer 32 bit_. 223 | -------------------------------------------------------------------------------- /content/chapter-2/3-comments.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Comments' 3 | metaTitle: 'Comments' 4 | metaDescription: 'Comments' 5 | --- 6 | 7 | _Comment_ adalah satu hal yang cukup penting dan perlu ada disetiap bahasa program, dengan fitur _comment_, _developer_ bisa dengan mudah meninggalkan catatan apapun di dalam setiap baris kode yang ditulis, untuk membuat komentar cukup dengan cara seperti ini: `// ini komen` 8 | 9 | Contoh: 10 | 11 | ```rust 12 | // ini komen 13 | fn main() { 14 | println!("Hallo Adiatma"); 15 | } 16 | ``` 17 | -------------------------------------------------------------------------------- /content/chapter-2/4-control-flow.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Control Flow' 3 | metaTitle: 'Control Flow' 4 | metaDescription: 'Control Flow' 5 | --- 6 | 7 | ## Control Flow 8 | 9 | Control flow adalah cara untuk mengoperasikan kondisi di Rust. Pengkondisian bukanlah hal yang baru dan sudah umum digunakan di setiap bahasa program, berikut adalah beberapa cara untuk melakukan pengkondisian. 10 | 11 | ### If Expressions 12 | 13 | Standarnya `if` adalah expression, karena `if` mengevaluasi dan mengembalikan sebuah nilai berdasarkan statement penentu. 14 | 15 | ```rust 16 | fn main() { 17 | if true { 18 | hello("Adiatma"); 19 | } 20 | } 21 | 22 | fn hello(name: &str) { 23 | println!("{}", name); 24 | } 25 | ``` 26 | 27 | ### Multiple Conditions 28 | 29 | Selain `if` terdapat juga beberapa kondisi lainnya yakni seperti `else if` dan `else`. Hal ini umum sama seperti bahasa yang lainnya. 30 | 31 | ```rust 32 | fn main() { 33 | let number = 10; 34 | 35 | if number > 5 { 36 | println!("more than five"); // output: more than five 37 | } else if number <= 5 { 38 | println!("number is five or lower than five"); 39 | } else { 40 | println!("Other"); 41 | } 42 | } 43 | ``` 44 | 45 | ### Menggunakan if di dalam sebuah let statement 46 | 47 | Walaupun `if` adalah sebuah _expression_, tetapi kita masih diperbolehkan untuk menggunakan `if` di dalam sebuah _statement_. Dengan syarat tipe data dari _statement_ harus sama, hal ini tujuannya untuk menjaga konsistensi data. 48 | 49 | ```rust 50 | fn main() { 51 | let n = 10; 52 | 53 | let number_condition = if n <= 5 { 54 | 5 55 | } else { 56 | 10 57 | }; 58 | 59 | println!("The value of number is: {}", number_condition); // 10 60 | } 61 | ``` 62 | -------------------------------------------------------------------------------- /content/chapter-2/5-perulangan.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Perulangan' 3 | metaTitle: 'Perulangan' 4 | metaDescription: 'Perulangan' 5 | --- 6 | 7 | ## Perulangan 8 | 9 | Dengan baris kode seperti di atas yang terjadi adalah perulangan kode dengan mencetak perintah “again!” berkali-kali tanpa berhenti, atau bisa disebut sebagai _overloop_. 10 | 11 | ## Loop 12 | 13 | Untuk melakukan operasi kode secara berulang salah satunya bisa dengan menggunakan `loop`. 14 | 15 | ```rust 16 | fn main() { 17 | loop { 18 | println!("again!"); 19 | } 20 | } 21 | ``` 22 | 23 | Dengan baris kode seperti di atas yang terjadi adalah perulangan kode dengan mencetak perintah “again!” berkali - kali tanpa berhenti, atau bisa disebut sebagai _overloop_. 24 | 25 | 26 | ## While 27 | 28 | Setelah mengenal `loop` selanjutnya adalah `while`, berbeda dengan yang sebelumnya, _while_ akan terus berulang jika kondisinya `true`, jadi kita bisa melakukan operasi yang berulang berdasarkan kondisi yang kita tentukan. 29 | 30 | ```rust 31 | fn main() { 32 | let mut number = 3; 33 | 34 | while number != 0 { 35 | println!("{}!", number); 36 | 37 | number -= 1; 38 | } 39 | 40 | println!("LIFTOFF!!!"); 41 | } 42 | ``` 43 | 44 | ## For 45 | 46 | Selanjutnya adalah `for` untuk perulangan, berbeda seperti `while`, `for` biasanya digunakan untuk mengulang beberapa koleksi data, seperti dibawah ini. Lebih efektif dibandingkan dengan `while`. 47 | 48 | ```rust 49 | fn main() { 50 | let numbers = [1, 2, 3]; 51 | 52 | for number in numbers.iter() { 53 | println!("{}", number); 54 | } 55 | } 56 | ``` 57 | 58 | Kode diatas terlihat ada hal yang baru yaitu `iter()`, nah fungsi ini digunakan untuk mengkonversi _variabel_ _numbers_ yang bertipe data _array_ menjadi **_iterator_**. Agar bisa diambil item-itemnya di dalam sebuah perulangan. 59 | 60 | -------------------------------------------------------------------------------- /content/chapter-2/6-struct.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Struct' 3 | metaTitle: 'Struct' 4 | metaDescription: 'Struct' 5 | --- 6 | 7 | ## Struct 8 | 9 | _Struct_ atau pendek kata dari _structure_ biasanya digunakan untuk custom tipe data, _struct_ sedikit mirip tipe data _object_. Dengan menggunakan _struct_ kita bisa menulis kode dengan paradigma **Object Oriented Programing (OOP)**. 10 | 11 | Contoh Tipe Data _Struct_ 12 | 13 | Untuk inisialisasi tipe data struct bisa menggunakan _keyword_ `struct` dan kemudian nama tipe datanya. 14 | 15 | ```rust 16 | struct People { 17 | name: String, 18 | age: i32, 19 | } 20 | ``` 21 | 22 | Berikut adalah contoh penggunaan `struct`, dengan menyimpan `struct People {}` kedalam sebuah variabel `p`. 23 | 24 | ```rust 25 | struct People { 26 | name: String, 27 | age: i32, 28 | } 29 | 30 | fn main() { 31 | let p = People { 32 | name: "Adiatma".to_string(), 33 | age: 22, 34 | }; 35 | 36 | println!("Hy {}, {}", p.name, p.age); // Adiatma 22 37 | } 38 | ``` 39 | 40 | Pada kode diatas terlihat `struct People` di inisialisasi dan kemudian diisi dengan dua tipe data yaitu `String` dan signed integer 32 bit atau `i32`. Nah kemudian di dalam `function main` terdapat sebuah variabel yang mereferensi ke `struct People` dan menambahkan nilai berdasarkan tipe data pada `struct People`, dan di akhir baris kode variabel `p` telah memiliki tipe data dan nilai dari `struct People` sehingga dapat dibaca dengan menggunakan _dot notation_ untuk mencetak secara spesifik nilai dari `struct` tersebut. 41 | 42 | ## Method 43 | 44 | _Method_ secara syntax terlihat mirip seperti _function_, diinisiasi dengan menggunakan _keyword_ `fn` dan kemudian di depannya ada nama method. Hal yang membedakan terletak pada konteksnya. Method berada di dalam konteks `struct`, seperti contoh dibawah ini. 45 | 46 | ```rust 47 | struct People { 48 | first_name: String, 49 | last_name: String 50 | } 51 | 52 | impl People { 53 | fn get_fullname(&self) -> String { 54 | format!("{} {}", self.first_name, self.last_name) 55 | } 56 | } 57 | 58 | fn main() { 59 | let p = People { 60 | first_name: "Adiatma".to_string(), 61 | last_name: "Kamarudin".to_string(), 62 | }; 63 | 64 | println!("{}", p.get_fullname()) // "Adiatma Kamarudin" 65 | } 66 | ``` 67 | _Block_ `impl` pada contoh di atas mendeskripsikan konteks pada `struct`. Kemudian _function_ yang direferensikan ke konteks disisipkan di dalam _block_ `impl`. 68 | 69 | ## Method dengan lebih dari satu parameter 70 | 71 | _Syntax_ `Self` pada _method_ akan mereferensi ke `struct People`, atau konteksnya sendiri, sehingga bisa mengakses nilai-nilai yang ada pada _People_. 72 | 73 | ```rust 74 | impl People { 75 | fn add_new_identity(&self, other: String) -> String { 76 | format!("{} {} {}", self.first_name, self.last_name, other) 77 | } 78 | } 79 | ``` 80 | ## Associated Functions 81 | 82 | _Block_ `impl` dapat digunakan tanpa memanfaatkan _syntax_ `self` sebagai parameter _method_. Konsep ini dinamakan _associated functions_. _Associated functions_ merupakan _function_ yang diasosiasikan dengan _struct_ namun bukan merupakan _method struct_. _Associated functions_ digunakan untuk _constructor_ yang akan mengembalikan _instance_ baru dari sebuah _struct_. Adapun contoh implementasi _associated functions_ untuk _struct_ seperti berikut. 83 | 84 | ```rust 85 | impl People { 86 | fn count_income(d: i32, i: i32) -> People { 87 | People { 88 | days: d, 89 | income: i 90 | } 91 | } 92 | } 93 | 94 | ``` 95 | Untuk memanggil kembali _associated functions_ yang telah dibuat dapat menerapkan _syntax_ `::` seperti `People::count_income(30, 3000)`. Adapun salah satu contoh menampilkan pemanggilan _associated functions_ adalah sebagai berikut. 96 | 97 | ```rust 98 | fn main() { 99 | let show = People::count_income(30, 3000); 100 | println!("{:#?}", show); 101 | } 102 | ``` -------------------------------------------------------------------------------- /content/chapter-2/7-enum-and-pattern-matching.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Enum & Pattern Matching' 3 | metaTitle: 'Enum & Pattern Matching' 4 | metaDescription: 'Enum & Pattern Matching' 5 | --- 6 | 7 | ## Enum 8 | 9 | _Enum_ adalah fitur yang disediakan untuk membuat beberapa opsi pilihan berdasarkan varian yang ada. Untuk mendefinisikan _enum_ cukup menggunakan _keyword_ **`enum`** seperti dibawah ini. 10 | 11 | ```rust 12 | enum IpAddress { 13 | V4, 14 | V6 15 | } 16 | ``` 17 | Perbedaan yang mendasari antara _enum_ dengan _struct_ yaitu, _enum_ dapat menyimpan data dengan variasi tipe data berbeda. Untuk mengakses nilai _enum_ bisa dengan cara seperti ini. 18 | 19 | ```rust 20 | let IpV4 = IpAddress::V4; 21 | ``` 22 | 23 | Untuk penggunaan _enum_ biasanya cocok dipadukan dengan _pattern matching_ atau pola yang cocok jika diterjemahkan ke bahasa indonesia. 24 | 25 | ## Pattern Matching 26 | 27 | Rust punya _control flow operator_ yang cukup _powerful_, yakni dikenal dengan “match” fitur ini bisa digunakan untuk membaca nilai pada sebuah opsi _enum_ dan mengembalikan nilai yang ada berdasarkan kecocokan, untuk lebih lengkapnya berikut adalah contoh kodenya. 28 | 29 | ```rust 30 | #[derive(Debug)] 31 | enum IpAddress { 32 | V4, 33 | V6 34 | } 35 | 36 | fn ip_print(ip_address: IpAddress) -> i32 { 37 | match ip_address { 38 | IpAddress::V4 => 1, 39 | IpAddress::V6 => 2, 40 | _ => 0 41 | } 42 | } 43 | 44 | fn main() { 45 | println!("{:?} {:?}", ip_print(IpAddress::V4), ip_print(IpAddress::V6)); 46 | } 47 | ``` 48 | 49 | Kode diatas terlihat sebuah `enum IpAddress` dan juga sebuah _function_ `is_print(ip_address: IpAddress)` yang tujuannya untuk mencetak nilai _enum_ dengan sebuah parameter yang diinputkan, jika nilai nya cocok dengan opsi yang ada maka akan dikembalikan nilainya. 50 | -------------------------------------------------------------------------------- /content/chapter-2/8-ownership.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Ownership' 3 | metaTitle: 'Ownership' 4 | metaDescription: 'Ownership' 5 | --- 6 | 7 | ## Ownership 8 | 9 | Rust punya fitur utama yang dikenal dengan "ownership". Fitur _ownership_ merupakan sistem untuk mengatur alokasi memori di Rust, sehingga penggunaan memori menjadi maksimal. Rust tidak memiliki _garbage collector_. karena Rust kompilator akan mengecek kode kita di _runtime_ saat proses kompilasi kode berlangsung. 10 | 11 | Untuk memahami konsep _ownership_ kita perlu paham dulu bagian dari memori yang dikenal dengan _heap_ dan _stack_. 12 | 13 | ## Heap & Stack 14 | 15 | _Stack_ dan _heap_ memiliki struktur yang berbeda, _stack_ menyimpan data yang diketahui, biasanya ukurannya harus pasti dan tidak berubah-ubah, sehingga akses data di _stack_ lebih cepat, berbeda dengan _heap_ yang butuh alokasi tempat yang cukup besar di memori karena kepastian datanya bisa berubah-ubah. 16 | 17 | ## Ownership Rules 18 | 19 | - Nilai di Rust memiliki _variable_ _owner_ sendiri atau pemilik data tunggal. 20 | - Hanya ada satu _owner_. 21 | - _Owner_ hanya akan valid di dalam ruang lingkupnya, jika berada diluar maka tidak akan valid lagi. 22 | 23 | ## References & Borrowing 24 | 25 | Setelah membahas tentang _ownership_ di Rust, selanjutnya ada bahasan tentang “_References_ & _Borrowing”_ hal ini sangat berkaitan dengan materi _ownership_ atau bisa dibilang bagian dari _ownership_. 26 | 27 | Kenapa Rust menggunakan istilah _references_ & _borrowing_, karena konsep ini menjelaskan tentang perbedaan antara pemilik dan peminjam. Syaratnya data hanya punya satu pemilik tunggal, jika datanya beralih ke tangan yang lain, peminjam hanya berhak untuk meminjam sementara, dan tidak memiliki akses full terhadap apa yang dipinjam, sehingga hanya akan ada satu pemilik data yang tersimpan di memori, hal ini mencegah _race condition_ dalam perubahan data. 28 | 29 | References itu standarnya _immutable_, syntax nya seperti ini `&T`. Di Rust bisa merubah sifat _references_ dari _immutable_ ke _mutable_, istilah ini dikenal dengan _mutable references_, syntax nya seperti ini `&mut T` cukup menambahkan `mut`. 30 | 31 | ```rust 32 | fn main() { 33 | let x = 1; 34 | let b = &x; // references 35 | let mut a = 2; 36 | let c = &mut a; // mutable references 37 | 38 | println!("{} {}", b, c); // 1 2 39 | } 40 | ``` 41 | -------------------------------------------------------------------------------- /content/chapter-2/9-slices.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Slices' 3 | metaTitle: 'Slices' 4 | metaDescription: 'Slices' 5 | --- 6 | 7 | ## Slices 8 | 9 | Tipe data lainnya yang tidak memiliki _ownership_ adalah _slice_, _Slices_ memberikan kemudahan untuk mereferensi terhadap urutan data yang berdekatan dalam sebuah koleksi data. 10 | 11 | ## String Slices 12 | 13 | _String slice_ adalah referensi kepada bagian dari _String_, sehingga penggunaan _slices_ menggunakan _references syntax_ seperti ini `&[..]`. 14 | 15 | ```rust 16 | fn main() { 17 | let name = String::from("Hello Word"); 18 | println!("{}", &name[0..2]); // He 19 | } 20 | ``` 21 | 22 | Kode di atas sama seperti mereferensi ke seluruh koleksi data pada _String_ yang mana nilainya adalah “Hello World”, dan kemudian mengambil posisi _index_ dari setiap huruf yang tersusun, huruf “H” posisinya berada _index_ 0, dan _index_ 2 adalah huruf “e”, sehingga hasil keluarannya adalah “He”. 23 | 24 | Penggunaan _slice_ dapat dengan menggunakan _brackets_ seperti ini `[start_index..last_index]` posisi awal diawali dengan `start_index`, dan di akhir _slice_ adalah `last_index`. 25 | -------------------------------------------------------------------------------- /content/chapter-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Chapter 3' 3 | metaTitle: 'Chapter 3 - Belajar Rust' 4 | metaDescription: 'Chapter 3 - Belajar Rust' 5 | --- 6 | 7 | ## Chapter 3 8 | 9 | - [Cargo](./chapter-3/1-cargo) 10 | - [Release to https://crates.io](./chapter-3/2-crates.io) 11 | -------------------------------------------------------------------------------- /content/chapter-3/1-cargo.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Cargo' 3 | metaTitle: 'Mengenal Packages, Crates & Modul' 4 | metaDescription: 'Mengenal Packages, Crates, & Module' 5 | --- 6 | 7 | ## Mengenal Packages, Crates, & Module 8 | 9 | Dalam _development_ software tentunya bertambahnya fitur akan seiring dengan bertambahnya jumlah kode, untuk itu Rust punya beberapa konsep yang mungkin untuk membuat sebuah komponen yang modular. 10 | 11 | * **_Packages_** adalah paket _skeleton_ _project_ yang siap pakai. 12 | * **_Crates_** adalah _root_ dari beberapa _module_, dan _function_ yang berelasi. 13 | * **_Modules_** & **_use_** untuk mengontrol ruang lingkup dan privasi dari _paths_ atau bagian dari sistem _module_. 14 | * **_Paths_** adalah sebuah file yang terisolasi untuk mengelompokkan _function_ dan _module_. 15 | 16 | ## Packages & Crates 17 | 18 | _Crates_ adalah kumpulan _modules_, dan _function_ yang saling berelasi di dalam sebuah _package_. 19 | 20 | ```bash 21 | cargo new my-project 22 | Created binary (application) `my-project` package 23 | ls my-project 24 | Cargo.toml 25 | src 26 | ls my-project/src 27 | main.rs 28 | ``` 29 | 30 | Pada contoh diatas perintah `cargo new my-project` akan menghasilkan _package_ `my-project` didalamnya terdapat direktori `src` yang berisi file `src/main.rs` dan file `Cargo.toml` yang berisi informasi paket pustaka yang kita gunakan, mirip seperti `package.json` jika menggunakan _NodeJS_. 31 | 32 | ## Modules 33 | 34 | Setelah membahas tentang _package_ dan _crate_ hal selanjutnya adalah _modules,_ kita akan bahas sedikit tentang bagian-bagian dari sebuah _module_. Hal yang pertama adalah _paths_ yang digunakan untuk mengelompokkan sebuah fungsi-fungsi dan _module_ dalam sebuah file. Selanjutnya ada **_use_** yang merupakan _keyword_ untuk memanggil atau membawa sebuah _path_ kedalam _scope_ yang berbeda, dan **_pub keyword_** yang akan membuat sebuah _item_ menjadi publik agar dapat diakses di _scope_ yang berbeda. 35 | 36 | _Modules_ membuat kita mudah untuk mengorganisasikan sebuah _function_ berdasarkan fungsinya dan dapat digunakan berkali-kali untuk meminimalisir pekerjaan kita. 37 | 38 | Oke, untuk selanjutnya kita akan membuat sebuah paket _library_ di Rust, silakan ketik perintah `cargo new --lib my-lib` yang secara otomatis akan membuat _skeleton project_ yang _support_ untuk menerapkan konsep _library_, dengan _option_ `--lib` file yang di _generate_ dalam direktori `src` adalah `src/lib.rs` berbeda dengan tanpa menggunakan `--lib`. 39 | 40 | 41 | ```rust 42 | // src/lib.rs 43 | pub mod restaurant { 44 | pub fn hello(name: String) -> String { 45 | name 46 | } 47 | } 48 | ``` 49 | 50 | Pada contoh kode diatas terdapat sebuah _module_ _**restaurant**_ dengan _function_ `hello()` di dalamnya. _Keyword_ **_pub_** digunakan untuk membuat _module_ tersebut bisa diakses dari _scope_ atau _path_ yang berbeda. 51 | 52 | Untuk menguji _module_ _**restaurant**_ tersebut silakan buat file `main.rs`, di dalam direktori `src/main.rs` isi dengan kode dibawah ini. 53 | 54 | 55 | ```rust 56 | // src/main.rs 57 | extern crate my_lib; 58 | 59 | use my_lib::restaurant; 60 | 61 | fn main() { 62 | let adi = restaurant::hello(String::from("adi")); 63 | println!("{}", adi); 64 | } 65 | ``` 66 | 67 | Pada kode diatas, kode `extern crate my_lib` bertujuan untuk mengambil _module_ yang berada di _paths_ `lib.rs` yang merupakan _root component_, kemudian ada _keyword_ `use` tujuannya untuk membawa _module_ _**restaurant**_ yang ada pada _package_ `my_lib`. 68 | 69 | _Paths_ untuk mereferensi ke sebuah item di dalam _module_ 70 | 71 | Ada dua _path_ yang terkenal di Rust, yaitu, **_absolute path_** & **_relative path,_** berikut penjelasannya. 72 | 73 | - **_Absolute path_** diawali dengan _keyword crate_ untuk mengambil modul dari _root component_. 74 | - **_Relative path_** menggunakan _self_ dan _super_ untuk mengambil dari _current module_. 75 | 76 | Dari kedua _absolute_ dan _relative_ _paths_ diatas selanjutnya untuk mengakses _module_ secara spesifik bisa menggunakan _semicolons_ `(::)` biar lebih `clear` berikut ada contohnya. 77 | 78 | ```rust 79 | // src/lib.rs 80 | pub mod restaurant { 81 | pub fn hello(name: String) -> String { 82 | name 83 | } 84 | } 85 | 86 | pub fn call_directly() { 87 | // Absolute Path 88 | crate::restaurant::hello(); 89 | 90 | // Relative Path 91 | self::restaurant::hello(); 92 | } 93 | ``` 94 | 95 | _Keyword_ **_pub_** yang terdapat diawal _module_ dan _function_ bertujuan untuk mengekspos _module_ dan _function_ agar bisa diakses di luar _scope_, karena secara _default module_ dan _function_ di Rust bersifat _private_. 96 | 97 | ## Memahami Penggunaan Use 98 | 99 | ```rust 100 | pub mod restaurant { 101 | pub fn hello(name: String) -> String { 102 | name 103 | } 104 | } 105 | 106 | use crate::restaurant; 107 | 108 | pub fn call_hello_from_restaurant() { 109 | restaurant::hello() 110 | } 111 | ``` 112 | 113 | Pada kode diatas, _syntax_ kode `use crate::restaurant;` digunakan untuk mengambil _module restaurant_ dari **_root module_** dengan menggunakan cara **_absolute paths._** 114 | -------------------------------------------------------------------------------- /content/chapter-3/2-crates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Crates" 3 | metaTitle: "Mengenal Packages, Crates & Modul" 4 | metaDescription: "Mengenal Packages, Crates, & Module" 5 | --- 6 | 7 | ## Introduksi 8 | 9 | Setelah memahami tentang bagaimana membuat project dengan menggunakan `cargo` kini saatnya untuk 10 | merelease project yang telah kita buat sebelumnya dengan menggunakan Rust. 11 | 12 | ## Sebelum Release 13 | 14 | Hal pertama yang perlu dilakukan adalah mendaftarkan akun di https://crates.io bisa login dengan menggunakan github kemudian kunjungi https://crates.io/me klik button new token untuk membuat token baru, jika sudah buka terminal dan login dengan perintah `cargo login ` isi argument `` dengan value token yang kalian dapatkan dari `https://crates.io/me`. 15 | 16 | Setelah berhasil login di local kalian, jika berhasil login hasinya API token tersebut akan tersimpan di `~/.cargo/credentials.toml`. 17 | 18 | ## Release Manual 19 | 20 | Sebelumnya pastikan dulu untuk melihat spesifikasi data atau biasa disebut sebagai manifest yang 21 | terdapat di `Cargo.toml`, pastikan untuk mengisi field author, license/license-file, dan description, kemudian jika sudah lengkap, silahkan ketik perintah publish dengan `cargo publish`, atau sangat direkomendasikan untuk menambahkan argument `cargo publish --dry-run` untuk memastikan tidak ada warning dan errors sebelum release ke crates. 22 | 23 | ## Release Otomatis dengan CI/CD 24 | 25 | Selain cara manual, release juga dapat dilakukan dengan menggunakan tools CI/CD seperti travis 26 | misalnya, contohnya seperti dibawah ini. 27 | 28 | 29 | ``` 30 | language: rust 31 | 32 | deploy: 33 | provider: cargo 34 | token: "YOUR_TOKEN" 35 | on: 36 | tags: true 37 | branch: master 38 | ``` 39 | -------------------------------------------------------------------------------- /content/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Prolog' 3 | metaTitle: 'Mini Ebook - Belajar Bahasa Pemrograman Rust' 4 | metaDescription: 'Mini Ebook - Belajar Bahasa Pemrograman Rust' 5 | --- 6 | 7 | ## Introduksi 8 | 9 | Hi guys, mini ebook ini ditujukan untuk tambahan referensi belajar bahasa pemrograman Rust dengan versi bahasa Indonesia, sehingga khasanah referensi dengan bahasa Indonesia semakin banyak, dengan banyaknya referensi harapannya akan ada banyak orang yang bisa menggunakan Rust. 10 | 11 | Seluruh konten yang terdapat di mini ebook ini dapat diakses oleh semua orang, karena dapat di akses secara bebas di publik, tentunya jika terdapat kesalahan dalam tulisan, atau jika ada perubahan konten akan sangat mudah untuk diubah atau diperbarui. 12 | 13 | Untuk lebih lanjut berkontribusi silahkan buat [_issue_](https://github.com/evilfactorylabs/belajar-rust/issues) atau [_pull request_](https://github.com/evilfactorylabs/belajar-rust/pulls). 14 | 15 | ## Chapter 16 | - [Chapter 1](./chapter-1) 17 | - [Chapter 2](./chapter-2) 18 | - [Chapter 3](./chapter-3) 19 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | export const onServiceWorkerUpdateReady = () => { 2 | const answer = window.confirm( 3 | `This page has been updated. Reload to display the latest version?` 4 | ) 5 | if (answer === true) { 6 | window.location.reload() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | const config = require('./config') 4 | const queries = require('./src/utils/algolia') 5 | 6 | const plugins = [ 7 | 'gatsby-plugin-sitemap', 8 | 'gatsby-plugin-sharp', 9 | { 10 | resolve: `gatsby-plugin-layout`, 11 | options: { 12 | component: require.resolve(`./src/templates/docs.js`) 13 | } 14 | }, 15 | 'gatsby-plugin-emotion', 16 | 'gatsby-plugin-remove-trailing-slashes', 17 | 'gatsby-plugin-react-helmet', 18 | { 19 | resolve: 'gatsby-source-filesystem', 20 | options: { 21 | name: 'docs', 22 | path: `${__dirname}/content/` 23 | } 24 | }, 25 | { 26 | resolve: 'gatsby-plugin-mdx', 27 | options: { 28 | gatsbyRemarkPlugins: [ 29 | { 30 | resolve: 'gatsby-remark-images', 31 | options: { 32 | maxWidth: 1035, 33 | sizeByPixelDensity: true 34 | } 35 | }, 36 | { 37 | resolve: 'gatsby-remark-copy-linked-files' 38 | } 39 | ], 40 | extensions: ['.mdx', '.md'] 41 | } 42 | }, 43 | { 44 | resolve: `gatsby-plugin-gtag`, 45 | options: { 46 | // your google analytics tracking id 47 | trackingId: config.gatsby.gaTrackingId, 48 | // Puts tracking script in the head instead of the body 49 | head: true, 50 | // enable ip anonymization 51 | anonymize: false 52 | } 53 | } 54 | ] 55 | 56 | // check and add algolia 57 | if ( 58 | config.header.search && 59 | config.header.search.enabled && 60 | config.header.search.algoliaAppId && 61 | config.header.search.algoliaAdminKey 62 | ) { 63 | plugins.push({ 64 | resolve: `gatsby-plugin-algolia`, 65 | options: { 66 | appId: config.header.search.algoliaAppId, // algolia application id 67 | apiKey: config.header.search.algoliaAdminKey, // algolia admin key to index 68 | queries, 69 | chunkSize: 10000 // default: 1000 70 | } 71 | }) 72 | } 73 | 74 | // check and add pwa functionality 75 | if (config.pwa && config.pwa.enabled && config.pwa.manifest) { 76 | plugins.push({ 77 | resolve: `gatsby-plugin-manifest`, 78 | options: { ...config.pwa.manifest } 79 | }) 80 | plugins.push({ 81 | resolve: 'gatsby-plugin-offline', 82 | options: { 83 | appendScript: require.resolve(`./src/custom-sw-code.js`) 84 | } 85 | }) 86 | } else { 87 | plugins.push('gatsby-plugin-remove-serviceworker') 88 | } 89 | 90 | module.exports = { 91 | pathPrefix: config.gatsby.pathPrefix, 92 | siteMetadata: { 93 | title: config.siteMetadata.title, 94 | description: config.siteMetadata.description, 95 | docsLocation: config.siteMetadata.docsLocation, 96 | ogImage: config.siteMetadata.ogImage, 97 | favicon: config.siteMetadata.favicon, 98 | logo: { 99 | link: config.header.logoLink ? config.header.logoLink : '/', 100 | image: config.header.logo 101 | }, // backwards compatible 102 | headerTitle: config.header.title, 103 | githubUrl: config.header.githubUrl, 104 | helpUrl: config.header.helpUrl, 105 | tweetText: config.header.tweetText, 106 | headerLinks: config.header.links, 107 | siteUrl: config.gatsby.siteUrl 108 | }, 109 | plugins: plugins 110 | } 111 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const startCase = require('lodash.startcase') 3 | const componentWithMDXScope = require('gatsby-plugin-mdx/component-with-mdx-scope') 4 | 5 | exports.createPages = ({ graphql, actions }) => { 6 | const { createPage } = actions 7 | return new Promise((resolve, reject) => { 8 | resolve( 9 | graphql( 10 | ` 11 | { 12 | allMdx { 13 | edges { 14 | node { 15 | fields { 16 | id 17 | } 18 | tableOfContents 19 | fields { 20 | slug 21 | } 22 | } 23 | } 24 | } 25 | } 26 | ` 27 | ).then(result => { 28 | if (result.errors) { 29 | console.log(result.errors) 30 | reject(result.errors) 31 | } 32 | 33 | // Create blog posts pages. 34 | result.data.allMdx.edges.forEach(({ node }) => { 35 | createPage({ 36 | path: node.fields.slug ? node.fields.slug : '/', 37 | component: path.resolve('./src/templates/docs.js'), 38 | context: { 39 | id: node.fields.id 40 | } 41 | }) 42 | }) 43 | }) 44 | ) 45 | }) 46 | } 47 | 48 | exports.onCreateWebpackConfig = ({ actions }) => { 49 | actions.setWebpackConfig({ 50 | resolve: { 51 | modules: [path.resolve(__dirname, 'src'), 'node_modules'], 52 | alias: { 53 | $components: path.resolve(__dirname, 'src/components'), 54 | buble: '@philpl/buble' // to reduce bundle size 55 | } 56 | } 57 | }) 58 | } 59 | 60 | exports.onCreateBabelConfig = ({ actions }) => { 61 | actions.setBabelPlugin({ 62 | name: '@babel/plugin-proposal-export-default-from' 63 | }) 64 | } 65 | 66 | exports.onCreateNode = ({ node, getNode, actions }) => { 67 | const { createNodeField } = actions 68 | 69 | if (node.internal.type === `Mdx`) { 70 | const parent = getNode(node.parent) 71 | let value = parent.relativePath.replace(parent.ext, '') 72 | 73 | if (value === 'index') { 74 | value = '' 75 | } 76 | 77 | createNodeField({ 78 | name: `slug`, 79 | node, 80 | value: `/${value}` 81 | }) 82 | 83 | createNodeField({ 84 | name: 'id', 85 | node, 86 | value: node.id 87 | }) 88 | 89 | createNodeField({ 90 | name: 'title', 91 | node, 92 | value: node.frontmatter.title || startCase(parent.name) 93 | }) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "public" 3 | command = "npm run build" 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@evilfactorylabs/handbook", 3 | "private": true, 4 | "description": "evilfactorylabs Handbook", 5 | "version": "1.1.0", 6 | "dependencies": { 7 | "@babel/plugin-proposal-export-default-from": "^7.7.4", 8 | "@emotion/core": "^10.0.22", 9 | "@emotion/styled": "^10.0.23", 10 | "@emotion/styled-base": "^10.0.24", 11 | "@mdx-js/loader": "^1.5.1", 12 | "@mdx-js/mdx": "^1.5.1", 13 | "@mdx-js/react": "^1.5.1", 14 | "@philpl/buble": "^0.19.7", 15 | "@playlyfe/gql": "^2.6.2", 16 | "algoliasearch": "^3.35.1", 17 | "dotenv": "^8.2.0", 18 | "emotion": "^10.0.23", 19 | "emotion-server": "^10.0.17", 20 | "emotion-theming": "^10.0.19", 21 | "gatsby": "^2.18.10", 22 | "gatsby-link": "^2.2.27", 23 | "gatsby-plugin-algolia": "^0.5.0", 24 | "gatsby-plugin-emotion": "^4.1.18", 25 | "gatsby-plugin-gtag": "^1.0.12", 26 | "gatsby-plugin-layout": "^1.1.18", 27 | "gatsby-plugin-manifest": "^2.2.33", 28 | "gatsby-plugin-mdx": "^1.0.61", 29 | "gatsby-plugin-offline": "^3.0.29", 30 | "gatsby-plugin-react-helmet": "^3.1.18", 31 | "gatsby-plugin-remove-serviceworker": "^1.0.0", 32 | "gatsby-plugin-sharp": "^2.3.7", 33 | "gatsby-plugin-sitemap": "^2.2.24", 34 | "gatsby-remark-copy-linked-files": "^2.1.33", 35 | "gatsby-remark-images": "^3.1.37", 36 | "gatsby-source-filesystem": "^2.1.42", 37 | "gatsby-transformer-remark": "^2.6.42", 38 | "graphql": "^14.5.8", 39 | "is-absolute-url": "^3.0.3", 40 | "lodash.flatten": "^4.4.0", 41 | "lodash.startcase": "^4.4.0", 42 | "react": "^16.12.0", 43 | "react-dom": "^16.12.0", 44 | "react-feather": "^2.0.3", 45 | "react-github-btn": "^1.1.1", 46 | "react-helmet": "^5.2.1", 47 | "react-id-generator": "^3.0.0", 48 | "react-instantsearch-dom": "^6.0.0", 49 | "react-live": "^2.2.2", 50 | "react-loadable": "^5.5.0", 51 | "styled-components": "^4.4.1", 52 | "styled-icons": "^9.0.1" 53 | }, 54 | "license": "MIT", 55 | "scripts": { 56 | "start": "gatsby develop", 57 | "build": "gatsby build --prefix-paths" 58 | }, 59 | "devDependencies": { 60 | "gatsby-plugin-remove-trailing-slashes": "^2.1.17", 61 | "prism-react-renderer": "^1.0.2" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/CommunityAuthor.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import './components/styles.css' 4 | 5 | const CommunityAuthor = ({ 6 | name, 7 | imageUrl, 8 | twitterUrl, 9 | githubUrl, 10 | description 11 | }) => { 12 | return ( 13 | <> 14 |

About the community author

15 |
16 |
17 | {name} 18 |
19 |
20 |
21 | {name} 22 | {twitterUrl ? ( 23 | 24 | Twitter Icon 29 | 30 | ) : null} 31 | {githubUrl ? ( 32 | 33 | Github Icon 38 | 39 | ) : null} 40 |
41 |
{description}
42 |
43 |
44 | 45 | ) 46 | } 47 | 48 | export default CommunityAuthor 49 | -------------------------------------------------------------------------------- /src/GithubLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | const githubIcon = require('./components/images/github.svg') 3 | 4 | import './components/styles.css' 5 | 6 | const GithubLink = ({ link, text }) => { 7 | return ( 8 | 9 | github 10 | {text} 11 | 12 | ) 13 | } 14 | 15 | export default GithubLink 16 | -------------------------------------------------------------------------------- /src/YoutubeEmbed.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import './components/styles.css' 4 | 5 | const YoutubeEmbed = ({ link }) => { 6 | return ( 7 |
8 | 16 |
17 | ) 18 | } 19 | 20 | export default YoutubeEmbed 21 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { StaticQuery, graphql } from 'gatsby' 3 | import GitHubButton from 'react-github-btn' 4 | import Link from './link' 5 | import './styles.css' 6 | import config from '../../config.js' 7 | 8 | import Loadable from 'react-loadable' 9 | import LoadingProvider from './mdxComponents/loading' 10 | 11 | const help = require('./images/help.svg') 12 | const isSearchEnabled = 13 | config.header.search && config.header.search.enabled ? true : false 14 | 15 | let searchIndices = [] 16 | if (isSearchEnabled && config.header.search.indexName) { 17 | searchIndices.push({ 18 | name: `${config.header.search.indexName}`, 19 | title: `Results`, 20 | hitComp: `PageHit` 21 | }) 22 | } 23 | 24 | import Sidebar from './sidebar' 25 | 26 | const LoadableComponent = Loadable({ 27 | loader: () => import('./search/index'), 28 | loading: LoadingProvider 29 | }) 30 | 31 | function myFunction() { 32 | var x = document.getElementById('navbar') 33 | if (x.className === 'topnav') { 34 | x.className += ' responsive' 35 | } else { 36 | x.className = 'topnav' 37 | } 38 | } 39 | 40 | const Header = ({ location }) => ( 41 | { 63 | const logoImg = require('./images/logo.svg') 64 | const twitter = require('./images/twitter.svg') 65 | const { 66 | site: { 67 | siteMetadata: { 68 | headerTitle, 69 | githubUrl, 70 | helpUrl, 71 | tweetText, 72 | logo, 73 | headerLinks 74 | } 75 | } 76 | } = data 77 | const finalLogoLink = logo.link !== '' ? logo.link : '/' 78 | return ( 79 |
80 | 170 |
171 | ) 172 | }} 173 | /> 174 | ) 175 | 176 | export default Header 177 | -------------------------------------------------------------------------------- /src/components/NextPrevious.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Link from './link' 3 | 4 | import './styles.css' 5 | 6 | class NextPrevious extends React.Component { 7 | render() { 8 | const { mdx, nav } = this.props 9 | let currentIndex 10 | const currentPaginationInfo = nav.map((el, index) => { 11 | if (el && el.url === mdx.fields.slug) { 12 | currentIndex = index 13 | } 14 | }) 15 | const nextInfo = {} 16 | const previousInfo = {} 17 | if (currentIndex === undefined) { 18 | // index 19 | if (nav[0]) { 20 | nextInfo.url = nav[0].url 21 | nextInfo.title = nav[0].title 22 | } 23 | previousInfo.url = null 24 | previousInfo.title = null 25 | currentIndex = -1 26 | } else if (currentIndex === 0) { 27 | // first page 28 | nextInfo.url = nav[currentIndex + 1] ? nav[currentIndex + 1].url : null 29 | nextInfo.title = nav[currentIndex + 1] 30 | ? nav[currentIndex + 1].title 31 | : null 32 | previousInfo.url = null 33 | previousInfo.title = null 34 | } else if (currentIndex === nav.length - 1) { 35 | // last page 36 | nextInfo.url = null 37 | nextInfo.title = null 38 | previousInfo.url = nav[currentIndex - 1] 39 | ? nav[currentIndex - 1].url 40 | : null 41 | previousInfo.title = nav[currentIndex - 1] 42 | ? nav[currentIndex - 1].title 43 | : null 44 | } else if (currentIndex) { 45 | // any other page 46 | nextInfo.url = nav[currentIndex + 1].url 47 | nextInfo.title = nav[currentIndex + 1].title 48 | if (nav[currentIndex - 1]) { 49 | previousInfo.url = nav[currentIndex - 1].url 50 | previousInfo.title = nav[currentIndex - 1].title 51 | } 52 | } 53 | return ( 54 |
55 | {previousInfo.url && currentIndex >= 0 ? ( 56 | 57 |
58 | 71 | 72 | 73 | 74 | 75 | 76 |
77 |
78 |
79 | Previous 80 |
81 |
82 | {nav[currentIndex - 1].title} 83 |
84 |
85 | 86 | ) : null} 87 | {nextInfo.url && currentIndex >= 0 ? ( 88 | 89 |
90 |
91 | Next 92 |
93 |
94 | 95 | {nav[currentIndex + 1] && nav[currentIndex + 1].title} 96 | 97 |
98 |
99 |
100 | 113 | 114 | 115 | 116 | 117 | 118 |
119 | 120 | ) : null} 121 |
122 | ) 123 | } 124 | } 125 | 126 | export default NextPrevious 127 | -------------------------------------------------------------------------------- /src/components/images/closed.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const ClosedSvg = () => ( 4 | 10 | 11 | 12 | ) 13 | 14 | export default ClosedSvg 15 | -------------------------------------------------------------------------------- /src/components/images/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/images/help.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 11 | 12 | 17 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/components/images/opened.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const OpenedSvg = () => ( 4 | 10 | 11 | 12 | ) 13 | 14 | export default OpenedSvg 15 | -------------------------------------------------------------------------------- /src/components/images/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | export theme from './theme' 2 | export mdxComponents from './mdxComponents' 3 | export ThemeProvider from './themeProvider' 4 | export Layout from './layout' 5 | export Link from './link' 6 | -------------------------------------------------------------------------------- /src/components/layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from '@emotion/styled' 3 | 4 | import { MDXProvider } from '@mdx-js/react' 5 | 6 | import ThemeProvider from './themeProvider' 7 | import mdxComponents from './mdxComponents' 8 | import Sidebar from './sidebar' 9 | import RightSidebar from './rightSidebar' 10 | 11 | const Wrapper = styled('div')` 12 | display: flex; 13 | justify-content: space-between; 14 | 15 | @media only screen and (max-width: 767px) { 16 | display: block; 17 | } 18 | ` 19 | 20 | const Content = styled('main')` 21 | display: flex; 22 | flex-grow: 1; 23 | margin: 0px 88px; 24 | margin-top: 3rem; 25 | 26 | @media only screen and (max-width: 1023px) { 27 | padding-left: 0; 28 | margin: 0 10px; 29 | margin-top: 3rem; 30 | } 31 | ` 32 | 33 | const MaxWidth = styled('div')` 34 | @media only screen and (max-width: 50rem) { 35 | width: 100%; 36 | position: relative; 37 | } 38 | ` 39 | const LeftSideBarWidth = styled('div')` 40 | width: 220px; 41 | ` 42 | const RightSideBarWidth = styled('div')` 43 | width: 224px; 44 | ` 45 | const Layout = ({ children, location }) => ( 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {children} 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ) 62 | 63 | export default Layout 64 | -------------------------------------------------------------------------------- /src/components/link.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import isAbsoluteUrl from 'is-absolute-url' 3 | 4 | import { Link as GatsbyLink } from 'gatsby' 5 | 6 | const Link = ({ to, ...props }) => 7 | isAbsoluteUrl(to) ? ( 8 | 9 | ) : ( 10 | 11 | ) 12 | 13 | export default Link 14 | -------------------------------------------------------------------------------- /src/components/mdxComponents/LiveProvider.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import { LiveProvider, LiveEditor, LiveError, LivePreview } from 'react-live' 4 | 5 | const ReactLiveProvider = ({ code }) => { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ) 13 | } 14 | 15 | export default ReactLiveProvider 16 | -------------------------------------------------------------------------------- /src/components/mdxComponents/anchor.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | const AnchorTag = ({ children: link, ...props }) => { 4 | if (link) { 5 | return ( 6 | 7 | {link} 8 | 9 | ) 10 | } else { 11 | return null 12 | } 13 | } 14 | 15 | export default AnchorTag 16 | -------------------------------------------------------------------------------- /src/components/mdxComponents/codeBlock.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import Loadable from 'react-loadable' 3 | import prismTheme from 'prism-react-renderer/themes/vsDark' 4 | 5 | import Highlight, { defaultProps } from 'prism-react-renderer' 6 | 7 | import LoadingProvider from './loading' 8 | 9 | import '../styles.css' 10 | 11 | /** Removes the last token from a code example if it's empty. */ 12 | function cleanTokens(tokens) { 13 | const tokensLength = tokens.length 14 | 15 | if (tokensLength === 0) { 16 | return tokens 17 | } 18 | 19 | const lastToken = tokens[tokensLength - 1] 20 | 21 | if (lastToken.length === 1 && lastToken[0].empty) { 22 | return tokens.slice(0, tokensLength - 1) 23 | } 24 | 25 | return tokens 26 | } 27 | 28 | const LoadableComponent = Loadable({ 29 | loader: () => import('./LiveProvider'), 30 | loading: LoadingProvider 31 | }) 32 | 33 | /* eslint-disable react/jsx-key */ 34 | const CodeBlock = ({ children: exampleCode, ...props }) => { 35 | if (props['react-live']) { 36 | return 37 | } else { 38 | return ( 39 | 45 | {({ className, style, tokens, getLineProps, getTokenProps }) => ( 46 |
 47 |             {cleanTokens(tokens).map((line, i) => {
 48 |               let lineClass = {}
 49 |               let isDiff = false
 50 |               if (
 51 |                 line[0] &&
 52 |                 line[0].content.length &&
 53 |                 line[0].content[0] === '+'
 54 |               ) {
 55 |                 lineClass = { backgroundColor: 'rgba(76, 175, 80, 0.2)' }
 56 |                 isDiff = true
 57 |               } else if (
 58 |                 line[0] &&
 59 |                 line[0].content.length &&
 60 |                 line[0].content[0] === '-'
 61 |               ) {
 62 |                 lineClass = { backgroundColor: 'rgba(244, 67, 54, 0.2)' }
 63 |                 isDiff = true
 64 |               } else if (
 65 |                 line[0] &&
 66 |                 line[0].content === '' &&
 67 |                 line[1] &&
 68 |                 line[1].content === '+'
 69 |               ) {
 70 |                 lineClass = { backgroundColor: 'rgba(76, 175, 80, 0.2)' }
 71 |                 isDiff = true
 72 |               } else if (
 73 |                 line[0] &&
 74 |                 line[0].content === '' &&
 75 |                 line[1] &&
 76 |                 line[1].content === '-'
 77 |               ) {
 78 |                 lineClass = { backgroundColor: 'rgba(244, 67, 54, 0.2)' }
 79 |                 isDiff = true
 80 |               }
 81 |               const lineProps = getLineProps({ line, key: i })
 82 |               lineProps.style = lineClass
 83 |               const diffStyle = {
 84 |                 userSelect: 'none',
 85 |                 MozUserSelect: '-moz-none',
 86 |                 WebkitUserSelect: 'none'
 87 |               }
 88 |               let splitToken
 89 |               return (
 90 |                 
91 | {line.map((token, key) => { 92 | if (isDiff) { 93 | if ( 94 | (key === 0 || key === 1) & 95 | (token.content.charAt(0) === '+' || 96 | token.content.charAt(0) === '-') 97 | ) { 98 | if (token.content.length > 1) { 99 | splitToken = { 100 | types: ['template-string', 'string'], 101 | content: token.content.slice(1) 102 | } 103 | const firstChar = { 104 | types: ['operator'], 105 | content: token.content.charAt(0) 106 | } 107 | return ( 108 | 109 | 113 | 116 | 117 | ) 118 | } else { 119 | return ( 120 | 124 | ) 125 | } 126 | } 127 | } 128 | return 129 | })} 130 |
131 | ) 132 | })} 133 |
134 | )} 135 |
136 | ) 137 | } 138 | } 139 | 140 | export default CodeBlock 141 | -------------------------------------------------------------------------------- /src/components/mdxComponents/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import CodeBlock from './codeBlock' 4 | import AnchorTag from './anchor' 5 | 6 | import '../styles.css' 7 | 8 | export default { 9 | h1: props => ( 10 |

15 | ), 16 | h2: props => ( 17 |

22 | ), 23 | h3: props => ( 24 |

29 | ), 30 | h4: props => ( 31 |

36 | ), 37 | h5: props => ( 38 |

43 | ), 44 | h6: props => ( 45 |
50 | ), 51 | p: props =>

, 52 | pre: props =>

,
53 |   code: CodeBlock,
54 |   a: AnchorTag
55 |   // TODO add `img`
56 |   // TODO add `blockquote`
57 |   // TODO add `ul`
58 |   // TODO add `li`
59 |   // TODO add `table`
60 | }
61 | 


--------------------------------------------------------------------------------
/src/components/mdxComponents/loading.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | 
3 | const LoadingProvider = ({ ...props }) => {
4 |   return 
5 | } 6 | 7 | export default LoadingProvider 8 | -------------------------------------------------------------------------------- /src/components/rightSidebar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { StaticQuery, graphql } from 'gatsby' 3 | 4 | import styled from '@emotion/styled' 5 | 6 | import Link from './link' 7 | import config from '../../config' 8 | 9 | import './styles.css' 10 | 11 | const forcedNavOrder = config.sidebar.forcedNavOrder 12 | 13 | const Sidebar = styled('aside')` 14 | width: 100%; 15 | background-color: #fff; 16 | border-right: 1px solid #ede7f3; 17 | height: 100vh; 18 | overflow: auto; 19 | position: fixed; 20 | padding-left: 24px; 21 | position: -webkit-sticky; 22 | position: -moz-sticky; 23 | position: sticky; 24 | top: 0; 25 | @media only screen and (max-width: 50rem) { 26 | width: 100%; 27 | position: relative; 28 | } 29 | ` 30 | 31 | // eslint-disable-next-line no-unused-vars 32 | const ListItem = styled(({ className, active, level, ...props }) => { 33 | return ( 34 |
  • 35 | 36 |
  • 37 | ) 38 | })` 39 | list-style: none; 40 | 41 | a { 42 | color: #5c6975; 43 | text-decoration: none; 44 | font-weight: ${({ level }) => (level === 0 ? 700 : 400)}; 45 | padding: 0.45rem 0 0.45rem ${props => 2 + (props.level || 0) * 1}rem; 46 | display: block; 47 | position: relative; 48 | 49 | &:hover { 50 | color: rgb(116, 76, 188) !important; 51 | } 52 | 53 | ${props => 54 | props.active && 55 | ` 56 | color: #663399; 57 | border-color: rgb(230,236,241) !important; 58 | border-style: solid none solid solid; 59 | border-width: 1px 0px 1px 1px; 60 | background-color: #fff; 61 | `} // external link icon 62 | svg { 63 | float: right; 64 | margin-right: 1rem; 65 | } 66 | } 67 | ` 68 | 69 | const SidebarLayout = ({ location }) => ( 70 | { 86 | let navItems = [] 87 | let finalNavItems 88 | if (allMdx.edges !== undefined && allMdx.edges.length > 0) { 89 | const navItems = allMdx.edges.map((item, index) => { 90 | let innerItems 91 | if (item !== undefined) { 92 | if ( 93 | item.node.fields.slug === location.pathname || 94 | config.gatsby.pathPrefix + item.node.fields.slug === 95 | location.pathname 96 | ) { 97 | if (item.node.tableOfContents.items) { 98 | innerItems = item.node.tableOfContents.items.map( 99 | (innerItem, index) => { 100 | const itemId = innerItem.title 101 | ? innerItem.title.replace(/\s+/g, '').toLowerCase() 102 | : '#' 103 | return ( 104 | 105 | {innerItem.title} 106 | 107 | ) 108 | } 109 | ) 110 | } 111 | } 112 | } 113 | if (innerItems) { 114 | finalNavItems = innerItems 115 | } 116 | }) 117 | } 118 | 119 | if (finalNavItems && finalNavItems.length) { 120 | return ( 121 | 122 |
      123 |
    • CONTENTS
    • 124 | {finalNavItems} 125 |
    126 |
    127 | ) 128 | } else { 129 | return ( 130 | 131 |
      132 |
      133 | ) 134 | } 135 | }} 136 | /> 137 | ) 138 | 139 | export default SidebarLayout 140 | -------------------------------------------------------------------------------- /src/components/search/hitComps.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react' 2 | import { Highlight, Snippet } from 'react-instantsearch-dom' 3 | import { Link } from 'gatsby' 4 | 5 | export const PageHit = clickHandler => ({ hit }) => ( 6 |
      7 | 8 |
      9 | 10 |
      11 | 12 | 13 |
      14 | ) 15 | -------------------------------------------------------------------------------- /src/components/search/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, createRef } from 'react' 2 | import { 3 | InstantSearch, 4 | Index, 5 | Hits, 6 | Configure, 7 | Pagination, 8 | connectStateResults 9 | } from 'react-instantsearch-dom' 10 | import { css } from '@emotion/core' 11 | import { Search } from 'styled-icons/fa-solid/Search' 12 | 13 | import styled from '@emotion/styled' 14 | import algoliasearch from 'algoliasearch/lite' 15 | 16 | import { PoweredBy } from './styles' 17 | 18 | import config from '../../../config.js' 19 | import Input from './input' 20 | import * as hitComps from './hitComps' 21 | 22 | import '../styles.css' 23 | 24 | const SearchIcon = styled(Search)` 25 | width: 1em; 26 | pointer-events: none; 27 | ` 28 | 29 | const HitsWrapper = styled.div` 30 | display: ${props => (props.show ? `grid` : `none`)}; 31 | max-height: 80vh; 32 | overflow: scroll; 33 | z-index: 2; 34 | -webkit-overflow-scrolling: touch; 35 | position: absolute; 36 | right: 0; 37 | top: calc(100% + 0.5em); 38 | width: 80vw; 39 | max-width: 30em; 40 | box-shadow: 0 0 5px 0; 41 | padding: 0.7em 1em 0.4em; 42 | background: white; 43 | @media only screen and (max-width: 991px) { 44 | width: 400px; 45 | max-width: 400px; 46 | } 47 | @media only screen and (max-width: 767px) { 48 | width: 100%; 49 | max-width: 500px; 50 | } 51 | border-radius: ${props => props.theme.smallBorderRadius}; 52 | > * + * { 53 | padding-top: 1em !important; 54 | border-top: 2px solid ${props => props.theme.darkGray}; 55 | } 56 | li + li { 57 | margin-top: 0.7em; 58 | padding-top: 0.7em; 59 | border-top: 1px solid ${props => props.theme.lightGray}; 60 | } 61 | * { 62 | margin-top: 0; 63 | padding: 0; 64 | color: black !important; 65 | } 66 | ul { 67 | list-style: none; 68 | } 69 | mark { 70 | color: ${props => props.theme.lightBlue}; 71 | background: ${props => props.theme.darkBlue}; 72 | } 73 | header { 74 | display: flex; 75 | justify-content: space-between; 76 | margin-bottom: 0.3em; 77 | h3 { 78 | color: black; 79 | background: ${props => props.theme.gray}; 80 | padding: 0.1em 0.4em; 81 | border-radius: ${props => props.theme.smallBorderRadius}; 82 | } 83 | } 84 | h3 { 85 | color: black; 86 | margin: 0 0 0.5em; 87 | } 88 | h4 { 89 | color: black; 90 | margin-bottom: 0.3em; 91 | } 92 | ` 93 | const Root = styled.div` 94 | position: relative; 95 | display: grid; 96 | grid-gap: 1em; 97 | @media only screen and (max-width: 767px) { 98 | width: 100%; 99 | } 100 | ` 101 | 102 | const Results = connectStateResults( 103 | ({ searching, searchState: state, searchResults: res }) => 104 | (searching && `Searching...`) || 105 | (res && res.nbHits === 0 && `No results for '${state.query}'`) 106 | ) 107 | 108 | const useClickOutside = (ref, handler, events) => { 109 | if (!events) events = [`mousedown`, `touchstart`] 110 | 111 | const detectClickOutside = event => 112 | ref && ref.current && !ref.current.contains(event.target) && handler() 113 | 114 | useEffect(() => { 115 | for (const event of events) 116 | document.addEventListener(event, detectClickOutside) 117 | return () => { 118 | for (const event of events) 119 | document.removeEventListener(event, detectClickOutside) 120 | } 121 | }) 122 | } 123 | 124 | export default function SearchComponent({ indices, collapse, hitsAsGrid }) { 125 | const ref = createRef() 126 | const [query, setQuery] = useState(``) 127 | const [focus, setFocus] = useState(false) 128 | const searchClient = algoliasearch( 129 | config.header.search.algoliaAppId, 130 | config.header.search.algoliaSearchKey 131 | ) 132 | 133 | useClickOutside(ref, () => setFocus(false)) 134 | 135 | const displayResult = 136 | query.length > 0 && focus ? 'showResults' : 'hideResults' 137 | return ( 138 | setQuery(query)} 142 | root={{ Root, props: { ref } }} 143 | > 144 | setFocus(true)} {...{ collapse, focus }} /> 145 | 0 && focus} 148 | asGrid={hitsAsGrid} 149 | > 150 | {indices.map(({ name, title, hitComp, type }) => { 151 | return ( 152 | 153 | 154 | setFocus(false))} /> 155 | 156 | ) 157 | })} 158 | 159 | 160 | 161 | 162 | ) 163 | } 164 | -------------------------------------------------------------------------------- /src/components/search/input.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connectSearchBox } from 'react-instantsearch-dom' 3 | 4 | import styled from '@emotion/styled' 5 | import { css } from '@emotion/core' 6 | import { Search } from 'styled-icons/fa-solid/Search' 7 | 8 | const SearchIcon = styled(Search)` 9 | width: 1em; 10 | pointer-events: none; 11 | margin-right: 10px; 12 | ` 13 | const focus = props => css` 14 | background: white; 15 | color: ${props => props.theme.darkBlue}; 16 | cursor: text; 17 | width: 5em; 18 | + ${SearchIcon} { 19 | color: ${props => props.theme.darkBlue}; 20 | margin: 0.3em; 21 | } 22 | ` 23 | const collapse = props => css` 24 | width: 0; 25 | cursor: pointer; 26 | color: ${props => props.theme.lightBlue}; 27 | + ${SearchIcon} { 28 | color: white; 29 | } 30 | ${props => props.focus && focus()} 31 | margin-left: ${props => (props.focus ? `-1.6em` : `-1em`)}; 32 | padding-left: ${props => (props.focus ? `1.6em` : `1em`)}; 33 | ::placeholder { 34 | color: ${props => props.theme.gray}; 35 | } 36 | ` 37 | const expand = props => css` 38 | background: ${props => props.theme.veryLightGray}; 39 | width: 6em; 40 | margin-left: -1.6em; 41 | padding-left: 1.6em; 42 | + ${SearchIcon} { 43 | margin: 0.3em; 44 | } 45 | ` 46 | 47 | const collapseExpand = props => css` 48 | ${props => (props.collapse ? collapse() : expand())} 49 | ` 50 | 51 | const Input = styled.input` 52 | outline: none; 53 | border: none; 54 | font-size: 1em; 55 | background: white; 56 | transition: ${props => props.theme.shortTrans}; 57 | border-radius: ${props => props.theme.smallBorderRadius}; 58 | {collapseExpand} 59 | ` 60 | const Form = styled.form` 61 | display: flex; 62 | flex-direction: row-reverse; 63 | align-items: center; 64 | @media only screen and (max-width: 767px) { 65 | width: 100%; 66 | margin-left: 15px; 67 | } 68 | ` 69 | 70 | export default connectSearchBox(({ refine, ...rest }) => { 71 | const preventSubmit = e => { 72 | e.preventDefault() 73 | } 74 | return ( 75 |
      76 | 77 | refine(e.target.value)} 83 | {...rest} 84 | /> 85 | 86 | ) 87 | }) 88 | -------------------------------------------------------------------------------- /src/components/search/styles.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Algolia } from 'styled-icons/fa-brands/Algolia' 3 | 4 | import '../styles.css' 5 | 6 | export const PoweredBy = () => ( 7 | 8 | Powered by{` `} 9 |
      10 | Algolia 11 | 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /src/components/sidebar/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from '@emotion/styled' 3 | 4 | import { ExternalLink } from 'react-feather' 5 | import { StaticQuery, graphql } from 'gatsby' 6 | 7 | import Tree from './tree' 8 | import config from '../../../config' 9 | 10 | import '../styles.css' 11 | 12 | const forcedNavOrder = config.sidebar.forcedNavOrder 13 | 14 | const Title = styled('h4')` 15 | color: white; 16 | padding: 10px; 17 | padding-left: 20px; 18 | font-size: 18px; 19 | font-weight: 900; 20 | line-height: 1.8rem; 21 | ` 22 | 23 | // eslint-disable-next-line no-unused-vars 24 | const ListItem = styled(({ className, active, level, ...props }) => { 25 | return ( 26 |
    • 27 | 28 |
    • 29 | ) 30 | })` 31 | list-style: none; 32 | 33 | a { 34 | color: #5c6975; 35 | text-decoration: none; 36 | font-weight: ${({ level }) => (level === 0 ? 700 : 400)}; 37 | padding: 0.45rem 0 0.45rem ${props => 2 + (props.level || 0) * 1}rem; 38 | display: block; 39 | position: relative; 40 | 41 | &:hover { 42 | color: rgb(116, 76, 188) !important; 43 | } 44 | 45 | ${props => 46 | props.active && 47 | ` 48 | color: #663399; 49 | border-color: rgb(230,236,241) !important; 50 | border-style: solid none solid solid; 51 | border-width: 1px 0px 1px 1px; 52 | background-color: #fff; 53 | `} // external link icon 54 | svg { 55 | float: right; 56 | margin-right: 1rem; 57 | } 58 | } 59 | ` 60 | 61 | const Sidebar = styled('aside')` 62 | width: 100%; 63 | /* background-color: rgb(245, 247, 249); */ 64 | /* border-right: 1px solid #ede7f3; */ 65 | height: 100vh; 66 | overflow: auto; 67 | position: fixed; 68 | padding-top: 10px; 69 | padding-left: 0px; 70 | position: -webkit-sticky; 71 | position: -moz-sticky; 72 | position: sticky; 73 | top: 0; 74 | margin-top: -60px; 75 | padding-right: 0; 76 | background-color: #3f0e40; 77 | z-index: 3; 78 | 79 | @media only screen and (max-width: 1023px) { 80 | width: 100%; 81 | /* position: relative; */ 82 | height: 100vh; 83 | } 84 | @media (min-width: 767px) and (max-width: 1023px) { 85 | padding-left: 0; 86 | } 87 | @media only screen and (max-width: 767px) { 88 | padding-left: 0px; 89 | background-color: #372476; 90 | background: #372476; 91 | height: auto; 92 | margin-top: 0; 93 | } 94 | ` 95 | 96 | const Divider = styled(props => ( 97 |
    • 98 |
      99 |
    • 100 | ))` 101 | list-style: none; 102 | padding: 0.5rem 0; 103 | 104 | hr { 105 | margin: 0; 106 | padding: 0; 107 | border: 0; 108 | margin-bottom: 1rem; 109 | } 110 | ` 111 | 112 | const SidebarLayout = ({ location }) => ( 113 | { 134 | return ( 135 | 136 |
      137 | {site.siteMetadata.headerTitle} 138 |
      139 |
        140 | 141 | 142 | {config.sidebar.links.map((link, key) => { 143 | if (link.link !== '' && link.text !== '') { 144 | return ( 145 | 146 | {link.text} 147 | 148 | 149 | ) 150 | } 151 | })} 152 |
      153 |
      154 | ) 155 | }} 156 | /> 157 | ) 158 | 159 | export default SidebarLayout 160 | -------------------------------------------------------------------------------- /src/components/sidebar/tree.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | import config from '../../../config' 4 | 5 | import TreeNode from './treeNode' 6 | 7 | const calculateTreeData = edges => { 8 | const originalData = config.sidebar.ignoreIndex 9 | ? edges.filter( 10 | ({ 11 | node: { 12 | fields: { slug } 13 | } 14 | }) => slug !== '/' 15 | ) 16 | : edges 17 | const tree = originalData.reduce( 18 | ( 19 | accu, 20 | { 21 | node: { 22 | fields: { slug, title } 23 | } 24 | } 25 | ) => { 26 | const parts = slug.split('/') 27 | let { items: prevItems } = accu 28 | for (const part of parts.slice(1, -1)) { 29 | let tmp = prevItems.find(({ label }) => label == part) 30 | if (tmp) { 31 | if (!tmp.items) { 32 | tmp.items = [] 33 | } 34 | } else { 35 | tmp = { label: part, items: [] } 36 | prevItems.push(tmp) 37 | } 38 | prevItems = tmp.items 39 | } 40 | const existingItem = prevItems.find( 41 | ({ label }) => label === parts[parts.length - 1] 42 | ) 43 | if (existingItem) { 44 | existingItem.url = slug 45 | existingItem.title = title 46 | } else { 47 | prevItems.push({ 48 | label: parts[parts.length - 1], 49 | url: slug, 50 | items: [], 51 | title 52 | }) 53 | } 54 | return accu 55 | }, 56 | { items: [] } 57 | ) 58 | const { 59 | sidebar: { forcedNavOrder = [] } 60 | } = config 61 | const tmp = [...forcedNavOrder] 62 | tmp.reverse() 63 | return tmp.reduce((accu, slug) => { 64 | const parts = slug.split('/') 65 | let { items: prevItems } = accu 66 | for (const part of parts.slice(1, -1)) { 67 | let tmp = prevItems.find(({ label }) => label == part) 68 | if (tmp) { 69 | if (!tmp.items) { 70 | tmp.items = [] 71 | } 72 | } else { 73 | tmp = { label: part, items: [] } 74 | prevItems.push(tmp) 75 | } 76 | prevItems = tmp.items 77 | } 78 | // sort items alphabetically. 79 | prevItems.map(item => { 80 | item.items = item.items.sort(function(a, b) { 81 | if (a.label < b.label) return -1 82 | if (a.label > b.label) return 1 83 | return 0 84 | }) 85 | }) 86 | const index = prevItems.findIndex( 87 | ({ label }) => label === parts[parts.length - 1] 88 | ) 89 | accu.items.unshift(prevItems.splice(index, 1)[0]) 90 | return accu 91 | }, tree) 92 | } 93 | 94 | const Tree = ({ edges }) => { 95 | const [treeData] = useState(() => { 96 | return calculateTreeData(edges) 97 | }) 98 | const defaultCollapsed = {} 99 | treeData.items.forEach(item => { 100 | if ( 101 | config.sidebar.collapsedNav && 102 | config.sidebar.collapsedNav.includes(item.url) 103 | ) { 104 | defaultCollapsed[item.url] = true 105 | } else { 106 | defaultCollapsed[item.url] = false 107 | } 108 | }) 109 | const [collapsed, setCollapsed] = useState(defaultCollapsed) 110 | const toggle = url => { 111 | setCollapsed({ 112 | ...collapsed, 113 | [url]: !collapsed[url] 114 | }) 115 | } 116 | return ( 117 | 125 | ) 126 | } 127 | 128 | export default Tree 129 | -------------------------------------------------------------------------------- /src/components/sidebar/treeNode.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Link from '../link' 4 | import OpenedSvg from '../images/opened' 5 | import ClosedSvg from '../images/closed' 6 | import config from '../../../config' 7 | 8 | const TreeNode = ({ 9 | className = '', 10 | setCollapsed, 11 | collapsed, 12 | url, 13 | title, 14 | items, 15 | ...rest 16 | }) => { 17 | const isCollapsed = collapsed[url] 18 | 19 | const collapse = () => { 20 | setCollapsed(url) 21 | } 22 | 23 | const hasChildren = items.length !== 0 24 | 25 | let location 26 | 27 | if (typeof document != 'undefined') { 28 | location = document.location 29 | } 30 | 31 | const active = 32 | location && 33 | (location.pathname === url || 34 | location.pathname === config.gatsby.pathPrefix + url) 35 | 36 | const calculatedClassName = `${className} item ${active ? 'active' : ''}` 37 | 38 | return ( 39 |
    • 40 | {title && ( 41 | 42 | {title} 43 | {!config.sidebar.frontLine && title && hasChildren ? ( 44 | 51 | ) : null} 52 | 53 | )} 54 | 55 | {!isCollapsed && hasChildren ? ( 56 |
        57 | {items.map(item => ( 58 | 64 | ))} 65 |
      66 | ) : null} 67 |
    • 68 | ) 69 | } 70 | 71 | export default TreeNode 72 | -------------------------------------------------------------------------------- /src/components/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Lato:300,400,700,900&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-display: swap; 8 | } 9 | ::-webkit-input-placeholder { 10 | /* Edge */ 11 | color: #c2c2c2; 12 | } 13 | 14 | :-ms-input-placeholder { 15 | /* Internet Explorer */ 16 | color: #c2c2c2; 17 | } 18 | 19 | ::placeholder { 20 | color: #c2c2c2; 21 | } 22 | html, 23 | body { 24 | font-family: 'Lato' !important; 25 | /* font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 26 | 'Roboto Light', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 27 | 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 28 | 'Segoe UI Symbol'; */ 29 | 30 | font-size: 16px; 31 | } 32 | 33 | a { 34 | transition: color 0.15s; 35 | color: #663399; 36 | } 37 | 38 | body { 39 | font-family: 'Roboto'; 40 | } 41 | .visibleMobile { 42 | display: none; 43 | } 44 | 45 | .video-responsive { 46 | position: relative; 47 | padding-bottom: 56.2%; 48 | } 49 | a { 50 | text-decoration: none; 51 | } 52 | a:hover { 53 | text-decoration: none; 54 | } 55 | .displayInline { 56 | display: inline-block; 57 | } 58 | .navBarToggle { 59 | border: 1px solid #fff; 60 | border-radius: 4px; 61 | width: 36px; 62 | height: 33px; 63 | position: absolute; 64 | right: 20px; 65 | padding: 8px 5px; 66 | display: none; 67 | background-color: #fff; 68 | } 69 | .navBarToggle .iconBar { 70 | display: block; 71 | width: 22px; 72 | height: 2px; 73 | border-radius: 1px; 74 | margin: 0 auto; 75 | margin-top: 4px; 76 | background-color: rgb(116, 76, 188); 77 | } 78 | .navBarToggle .iconBar:first-child { 79 | margin-top: 0px; 80 | } 81 | .video-responsive iframe { 82 | position: absolute; 83 | width: 100%; 84 | height: 100%; 85 | } 86 | 87 | .diffNewLine { 88 | color: #22863a; 89 | background-color: #f0fff4; 90 | } 91 | 92 | .diffRemoveLine { 93 | color: red; 94 | background-color: #ffcccc; 95 | } 96 | .navBarParent { 97 | width: 100%; 98 | float: left; 99 | display: flex; 100 | align-items: center; 101 | } 102 | .divider { 103 | height: 30px; 104 | margin: 0 15px; 105 | border-right: 1px solid rgba(255, 255, 255, 0.3); 106 | } 107 | .navBarULRight { 108 | /* position: absolute; 109 | right: 30px; */ 110 | } 111 | .githubIcon { 112 | width: 15px; 113 | margin-right: 5px; 114 | } 115 | 116 | .githubSection { 117 | display: flex; 118 | align-items: center; 119 | color: #000; 120 | opacity: 0.7; 121 | } 122 | 123 | .githubSection:hover { 124 | text-decoration: none; 125 | opacity: 1; 126 | } 127 | 128 | .navbar-default .navbar-toggle .icon-bar { 129 | background-color: #fff !important; 130 | } 131 | 132 | .navbar-default .navbar-toggle:focus, 133 | .navbar-default .navbar-toggle:hover { 134 | background-color: #1164a3; 135 | } 136 | 137 | .headerWrapper { 138 | /* border-bottom: 1px solid #eee; */ 139 | /* box-shadow: rgba(116, 129, 141, 0.1) 0px 1px 1px 0px; */ 140 | display: flex; 141 | align-items: center; 142 | } 143 | .formElement { 144 | background-color: transparent; 145 | padding: 4px; 146 | /* border-radius: 4px; */ 147 | border-bottom: 1px solid rgba(255, 255, 255, 0.3); 148 | } 149 | .formElement:focus { 150 | outline: none; 151 | border: none; 152 | } 153 | .formElement svg path { 154 | fill: #fff; 155 | } 156 | .searchInput { 157 | width: calc(100% - 26px); 158 | background-color: transparent !important; 159 | border-width: 0 !important; 160 | color: #c2c2c2; 161 | } 162 | .searchInput:focus, 163 | .searchInput:visited, 164 | .searchInput:hover, 165 | .searchInput:focus-within { 166 | outline: none; 167 | border: 0; 168 | } 169 | .searchWrapper { 170 | padding-left: 0px; 171 | padding-right: 20px; 172 | flex: 1; 173 | max-width: 600px; 174 | position: relative; 175 | } 176 | .searchWrapper a { 177 | font-weight: 500; 178 | } 179 | .hitWrapper { 180 | background-color: #fff; 181 | padding: 0.7em 1em 0.4em; 182 | border-radius: 4px; 183 | position: absolute; 184 | width: 80vw; 185 | max-width: 30em; 186 | top: 40px; 187 | border: 1px solid #ccc; 188 | box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.16); 189 | height: auto; 190 | max-height: 80vh; 191 | overflow: scroll; 192 | left: 0; 193 | } 194 | .hitWrapper ul li { 195 | margin-top: 0.7em; 196 | padding-top: 0.7em; 197 | border-top: 1px solid; 198 | list-style-type: none; 199 | } 200 | .hitWrapper ul li:first-child { 201 | border-top: 0px; 202 | margin-top: 0px; 203 | color: black !important; 204 | padding: 0px; 205 | } 206 | .showResults { 207 | display: block; 208 | } 209 | .hideResults { 210 | display: none; 211 | } 212 | .hitWrapper span { 213 | color: black; 214 | font-size: 14px; 215 | } 216 | .headerTitle { 217 | height: auto; 218 | font-size: 20px; 219 | line-height: 1; 220 | font-weight: bold; 221 | color: #333 !important; 222 | } 223 | .headerTitle a { 224 | color: #fff; 225 | } 226 | 227 | .headerTitle a:hover { 228 | text-decoration: none; 229 | opacity: 0.8; 230 | } 231 | .logoWrapper { 232 | padding: 21px 0; 233 | padding-left: 20px; 234 | } 235 | 236 | .logoContent { 237 | font-family: 'Roboto'; 238 | margin-left: 16px; 239 | font-size: 28px; 240 | line-height: 1.5; 241 | font-weight: 500; 242 | padding-right: 10px; 243 | } 244 | 245 | /* Header section starts here */ 246 | .navBarDefault { 247 | background-color: #fff; 248 | border-radius: 0; 249 | border-top: 0; 250 | margin-bottom: 0; 251 | border: 0; 252 | display: flex; 253 | align-items: center; 254 | /* border-bottom: 1px solid #eee; */ 255 | box-shadow: inset 0 -1px 0 0 rgba(29, 28, 29, 0.13); 256 | z-index: 1; 257 | padding: 10px 20px; 258 | position: relative; 259 | } 260 | .navBarHeader { 261 | min-width: 335px; 262 | padding-right: 20px; 263 | flex: 1; 264 | display: flex; 265 | align-items: center; 266 | min-height: 30px; 267 | } 268 | .navBarBrand { 269 | padding: 0px 0px; 270 | display: flex; 271 | align-items: center; 272 | } 273 | 274 | .navBarBrand img { 275 | width: 40px; 276 | margin-right: 16px; 277 | display: inline-block; 278 | } 279 | .navBarUL li { 280 | list-style-type: none; 281 | } 282 | .navBarUL { 283 | -webkit-overflow-scrolling: touch; 284 | } 285 | .navBarUL li a { 286 | font-family: 'Roboto'; 287 | color: #fff !important; 288 | font-size: 16px; 289 | font-weight: 500; 290 | line-height: 1em; 291 | opacity: 1; 292 | padding: 10px 15px; 293 | } 294 | .navBarNav { 295 | display: flex; 296 | align-items: center; 297 | } 298 | .navBarUL li a img, 299 | .navBarUL li a .shareIcon { 300 | width: 20px; 301 | } 302 | .mainWrapper { 303 | color: rgb(59, 69, 78); 304 | max-width: 750px; 305 | } 306 | .navBarUL li a:hover { 307 | opacity: 0.7; 308 | } 309 | pre { 310 | border: 0 !important; 311 | background-color: rgb(245, 247, 249); /* !important; */ 312 | } 313 | 314 | .mainWrapper ul, 315 | .mainWrapper ol { 316 | -webkit-padding-start: 40px; 317 | -moz-padding-start: 40px; 318 | -o-padding-start: 40px; 319 | margin: 24px 0px; 320 | padding: 0px 0px 0px 2em; 321 | } 322 | 323 | .mainWrapper ul li, 324 | .mainWrapper ol li { 325 | font-size: 16px; 326 | line-height: 1.8; 327 | font-weight: 400; 328 | } 329 | 330 | /* Header section ends here */ 331 | .headerNav { 332 | font-family: 'Roboto'; 333 | padding: 0px 24px; 334 | color: rgb(116, 76, 188); 335 | font-size: 16px; 336 | font-weight: 500; 337 | line-height: 1em; 338 | } 339 | 340 | .headerNav a { 341 | color: rgb(116, 76, 188); 342 | text-decoration: none; 343 | } 344 | 345 | .headerNav a:hover { 346 | text-decoration: none; 347 | } 348 | 349 | .logoWrapper img { 350 | width: 40px; 351 | } 352 | 353 | .sideBarUL { 354 | margin-top: 32px; 355 | } 356 | 357 | .sideBarUL li { 358 | list-style-type: none; 359 | width: auto; 360 | } 361 | 362 | .sideBarUL li a { 363 | color: rgb(207, 195, 207); 364 | font-size: 14px; 365 | font-weight: 500; 366 | line-height: 1.5; 367 | padding: 7px 24px 7px 16px; 368 | padding-left: 10px; 369 | padding-right: 25px; 370 | border-style: solid none solid solid; 371 | border-width: 1px 0px 1px 1px; 372 | border-color: transparent currentcolor transparent transparent; 373 | } 374 | 375 | .rightSideTitle { 376 | font-size: 10px; 377 | line-height: 1; 378 | font-weight: 700; 379 | text-transform: uppercase; 380 | letter-spacing: 1.2px; 381 | padding: 7px 24px 7px 16px; 382 | border-left: 1px solid #e6ecf1; 383 | border-left-color: rgb(230, 236, 241); 384 | color: #4c5767; 385 | } 386 | 387 | .rightSideBarUL { 388 | margin-top: 32px; 389 | } 390 | 391 | .rightSideBarUL li { 392 | list-style-type: none; 393 | border-left: 1px solid #e6ecf1; 394 | border-left-color: rgb(230, 236, 241); 395 | } 396 | 397 | .rightSideBarUL li a { 398 | font-size: 12px; 399 | font-weight: 500; 400 | line-height: 1.5; 401 | padding: 7px 24px 7px 16px; 402 | } 403 | 404 | .hideFrontLine .collapser { 405 | background: transparent; 406 | border: none; 407 | outline: none; 408 | position: absolute; 409 | right: 20px; 410 | z-index: 1; 411 | cursor: pointer; 412 | } 413 | 414 | .hideFrontLine .active > a { 415 | background-color: #1164a3; 416 | color: #fff !important; 417 | } 418 | .firstLevel ul li .collapser svg path { 419 | fill: #fff !important; 420 | } 421 | .active .collapser > svg > path { 422 | fill: #663399 !important; 423 | } 424 | 425 | /* .firstLevel ul .item ul .item { 426 | border-left: 1px solid #e6ecf1; 427 | } */ 428 | 429 | .sideBarUL .item { 430 | list-style: none; 431 | padding: 0; 432 | } 433 | .sideBarUL .item > a { 434 | color: #fff; 435 | text-decoration: none; 436 | display: flex; 437 | align-items: center; 438 | position: relative; 439 | width: 100%; 440 | padding-right: 35px; 441 | padding-left: 15px; 442 | } 443 | .sideBarUL .item > a:hover { 444 | background-color: #1164a3; 445 | color: #fff !important; 446 | } 447 | .showFrontLine .item > a:hover { 448 | background-color: #1164a3; 449 | } 450 | .showFrontLine .active > a { 451 | color: #fff; 452 | background-color: #473485; 453 | } 454 | 455 | /* .sideBarUL .item .item { 456 | margin-left: 16px; 457 | } 458 | 459 | .firstLevel > ul > .item { 460 | margin-left: 0 !important; 461 | } */ 462 | 463 | .sideBarUL .item ul .item ul .item a { 464 | padding-left: 30px; 465 | } 466 | 467 | .showFrontLine .item .item { 468 | border-left: 1px solid #e6ecf1; 469 | border-left-color: rgb(230, 236, 241); 470 | padding: 0; 471 | width: calc(100% - 16px) !important; 472 | } 473 | 474 | .showFrontLine .item .active > a { 475 | border-color: rgb(230, 236, 241) !important; 476 | border-style: solid none solid solid; 477 | border-width: 1px 0px 1px 1px; 478 | background-color: #1164a3; 479 | color: #fff; 480 | } 481 | 482 | .titleWrapper { 483 | display: flex; 484 | align-items: center; 485 | padding-bottom: 40px; 486 | border-bottom: 1px solid rgb(230, 236, 241); 487 | margin-bottom: 32px; 488 | } 489 | 490 | .title { 491 | font-size: 32px; 492 | line-height: 1.5; 493 | font-weight: 500; 494 | border-left: 2px solid rgb(116, 76, 188); 495 | padding: 0 16px; 496 | flex: 1; 497 | margin-top: 0; 498 | padding-top: 0; 499 | } 500 | 501 | .gitBtn { 502 | height: 30px; 503 | min-height: 30px; 504 | display: flex; 505 | align-items: center; 506 | } 507 | 508 | .gitBtn img { 509 | width: 15px; 510 | display: inline-block; 511 | margin-right: 5px; 512 | } 513 | 514 | .addPaddTopBottom { 515 | padding: 50px 0; 516 | } 517 | 518 | .nextPreviousWrapper { 519 | margin: 0px; 520 | padding: 0px; 521 | width: auto; 522 | display: grid; 523 | grid-template-rows: auto; 524 | column-gap: 24px; 525 | grid-template-columns: calc(50% - 8px) calc(50% - 8px); 526 | } 527 | 528 | .previousBtn { 529 | cursor: pointer; 530 | -moz-box-align: center; 531 | -moz-box-direction: normal; 532 | -moz-box-orient: horizontal; 533 | margin: 0px; 534 | padding: 0px; 535 | position: relative; 536 | display: flex; 537 | flex-direction: row; 538 | align-items: center; 539 | place-self: stretch; 540 | color: rgb(36, 42, 49); 541 | background-color: rgb(255, 255, 255); 542 | border-radius: 3px; 543 | border: 1px solid rgb(230, 236, 241); 544 | transition: border 200ms ease 0s; 545 | box-shadow: rgba(116, 129, 141, 0.1) 0px 3px 8px 0px; 546 | text-decoration: none; 547 | } 548 | 549 | .leftArrow { 550 | display: block; 551 | margin: 0px; 552 | color: rgb(157, 170, 182); 553 | flex: 0 0 auto; 554 | font-size: 24px; 555 | transition: color 200ms ease 0s; 556 | padding: 16px; 557 | padding-right: 16px; 558 | } 559 | 560 | .preRightWrapper { 561 | display: block; 562 | margin: 0px; 563 | flex: 1 1 0%; 564 | padding: 16px; 565 | text-align: right; 566 | } 567 | 568 | .smallContent { 569 | display: block; 570 | margin: 0px; 571 | padding: 0px; 572 | color: #6e6e6e; 573 | } 574 | 575 | .smallContent span { 576 | font-size: 12px; 577 | line-height: 1.625; 578 | font-weight: 400; 579 | } 580 | 581 | .nextPreviousTitle { 582 | display: block; 583 | margin: 0px; 584 | padding: 0px; 585 | transition: color 200ms ease 0s; 586 | } 587 | 588 | .nextPreviousTitle span { 589 | font-size: 16px; 590 | line-height: 1.5; 591 | font-weight: 500; 592 | } 593 | 594 | .nextBtn { 595 | cursor: pointer; 596 | -moz-box-align: center; 597 | -moz-box-direction: normal; 598 | -moz-box-orient: horizontal; 599 | margin: 0px; 600 | padding: 0px; 601 | position: relative; 602 | display: flex; 603 | flex-direction: row; 604 | align-items: center; 605 | place-self: stretch; 606 | color: rgb(36, 42, 49); 607 | background-color: rgb(255, 255, 255); 608 | border-radius: 3px; 609 | border: 1px solid rgb(230, 236, 241); 610 | transition: border 200ms ease 0s; 611 | box-shadow: rgba(116, 129, 141, 0.1) 0px 3px 8px 0px; 612 | text-decoration: none; 613 | } 614 | 615 | .rightArrow { 616 | flex: 0 0 auto; 617 | font-size: 24px; 618 | transition: color 200ms ease 0s; 619 | padding: 16px; 620 | padding-left: 16px; 621 | display: block; 622 | margin: 0px; 623 | color: rgb(157, 170, 182); 624 | } 625 | 626 | .nextRightWrapper { 627 | display: block; 628 | margin: 0px; 629 | padding: 16px; 630 | flex: 1 1 0%; 631 | } 632 | 633 | .nextBtn:hover, 634 | .previousBtn:hover { 635 | color: rgb(116, 76, 188); 636 | text-decoration: none; 637 | border: 1px solid rgb(116, 76, 188); 638 | } 639 | 640 | .nextBtn:hover .rightArrow, 641 | .previousBtn:hover .leftArrow { 642 | color: rgb(116, 76, 188); 643 | } 644 | 645 | /* tables.css */ 646 | table { 647 | padding: 0; 648 | } 649 | 650 | table tr { 651 | border-top: 1px solid #cccccc; 652 | background-color: white; 653 | margin: 0; 654 | padding: 0; 655 | } 656 | 657 | table tr:nth-child(2n) { 658 | background-color: #f8f8f8; 659 | } 660 | 661 | table tr th { 662 | font-weight: bold; 663 | border: 1px solid #cccccc; 664 | text-align: left; 665 | margin: 0; 666 | padding: 6px 13px; 667 | } 668 | 669 | table tr td { 670 | border: 1px solid #cccccc; 671 | text-align: left; 672 | margin: 0; 673 | padding: 6px 13px; 674 | } 675 | 676 | table tr th :first-child, 677 | table tr td :first-child { 678 | margin-top: 0; 679 | } 680 | 681 | table tr th :last-child, 682 | table tr td :last-child { 683 | margin-bottom: 0; 684 | } 685 | /* end - tables.css */ 686 | 687 | /* Image styling */ 688 | img { 689 | max-width: 100%; 690 | } 691 | /* end image */ 692 | .githubBtn { 693 | display: flex; 694 | align-items: center; 695 | font-size: 16px; 696 | padding: 10px 0px; 697 | padding-left: 15px; 698 | max-height: 40px; 699 | } 700 | .githubBtn span span { 701 | display: flex; 702 | align-items: center; 703 | } 704 | 705 | .communitySection { 706 | font-size: 24px; 707 | font-weight: 700; 708 | } 709 | .authorSection { 710 | padding: 20px 0; 711 | } 712 | .authorSection, 713 | .authorName { 714 | display: flex; 715 | align-items: center; 716 | } 717 | .authorImg img { 718 | width: 75px; 719 | height: 75px; 720 | border-radius: 50%; 721 | min-width: 75px; 722 | max-width: 75px; 723 | min-height: 75px; 724 | max-height: 75px; 725 | } 726 | .authorDetails { 727 | padding-left: 10px; 728 | } 729 | .authorDesc { 730 | padding-top: 5px; 731 | font-size: 14px; 732 | } 733 | .authorName img { 734 | margin-left: 10px; 735 | display: inline-block; 736 | width: 20px; 737 | } 738 | .authorName img:hover { 739 | opacity: 0.7; 740 | } 741 | 742 | .heading1 { 743 | font-size: 26px; 744 | font-weight: 800; 745 | line-height: 1.5; 746 | margin-bottom: 16px; 747 | margin-top: 32px; 748 | } 749 | 750 | .heading2 { 751 | font-size: 24px; 752 | font-weight: 700; 753 | line-height: 1.5; 754 | margin-bottom: 16px; 755 | margin-top: 32px; 756 | } 757 | 758 | .heading3 { 759 | font-size: 20px; 760 | font-weight: 600; 761 | line-height: 1.5; 762 | margin-bottom: 16px; 763 | margin-top: 32px; 764 | } 765 | 766 | .heading4 { 767 | font-size: 18px; 768 | font-weight: 500; 769 | line-height: 1.5; 770 | margin-bottom: 16px; 771 | margin-top: 32px; 772 | } 773 | 774 | .heading5 { 775 | font-size: 16px; 776 | font-weight: 400; 777 | line-height: 1.5; 778 | margin-bottom: 16px; 779 | margin-top: 32px; 780 | } 781 | 782 | .heading6 { 783 | font-size: 14px; 784 | font-weight: 300; 785 | line-height: 1.5; 786 | margin-bottom: 16px; 787 | margin-top: 32px; 788 | } 789 | 790 | .paragraph { 791 | margin: 16px 0px 32px; 792 | line-height: 1.625; 793 | } 794 | 795 | .pre { 796 | font-size: 14px; 797 | margin: 0px; 798 | padding: 16px; 799 | overflow: auto; 800 | } 801 | 802 | .mainWrapper code { 803 | background: #f9f7fb; 804 | border: 1px solid #ede7f3; 805 | border-radius: 4px; 806 | padding: 2px 6px; 807 | font-size: 0.9375em; 808 | } 809 | 810 | .poweredBy { 811 | font-size: 0.6em; 812 | text-align: end; 813 | padding: 0; 814 | } 815 | .topnav { 816 | -webkit-transition: top 0.5s, bottom 0.5s; 817 | } 818 | @media (max-width: 767px) { 819 | .responsive { 820 | margin-top: 15px; 821 | position: relative; 822 | padding-bottom: 20px; 823 | border-top: 1px solid #fff; 824 | } 825 | .headerTitle { 826 | padding-right: 50px; 827 | font-size: 20px; 828 | } 829 | .navBarBrand { 830 | min-height: 40px; 831 | } 832 | .navBarBrand img { 833 | margin-right: 8px; 834 | } 835 | .topnav.responsive .visibleMobile { 836 | display: block; 837 | } 838 | .topnav .navBarUL { 839 | display: none; 840 | } 841 | .topnav.responsive .navBarUL { 842 | display: block; 843 | text-align: left; 844 | } 845 | .hiddenMobile { 846 | display: none !important; 847 | } 848 | hr { 849 | margin-top: 0; 850 | margin-bottom: 0; 851 | } 852 | .navBarParent { 853 | display: block; 854 | } 855 | .separator { 856 | margin-top: 20px; 857 | margin-bottom: 20px; 858 | } 859 | .navBarULRight { 860 | position: static; 861 | } 862 | .navBarUL { 863 | display: flex; 864 | align-items: center; 865 | margin: 7.5px 0px; 866 | } 867 | .navBarUL li { 868 | height: 37px; 869 | } 870 | .navBarUL li a { 871 | font-size: 14px; 872 | padding: 10px 15px; 873 | } 874 | 875 | .navBarDefault { 876 | display: block; 877 | padding: 10px 0px; 878 | } 879 | 880 | .navBarToggle { 881 | margin-right: 0; 882 | display: block; 883 | } 884 | .navBarHeader { 885 | display: flex; 886 | min-width: auto; 887 | padding-right: 0; 888 | align-items: center; 889 | } 890 | .navBarBrand { 891 | font-size: 20px; 892 | padding: 0 0; 893 | padding-left: 0; 894 | flex: initial; 895 | padding-right: 15px; 896 | } 897 | 898 | .titleWrapper { 899 | padding: 0 15px; 900 | display: block; 901 | } 902 | 903 | .gitBtn { 904 | display: inline-block; 905 | } 906 | 907 | .mainWrapper { 908 | padding: 0 15px; 909 | } 910 | 911 | .nextPreviousWrapper { 912 | display: block; 913 | padding: 0 15px; 914 | } 915 | 916 | .previousBtn { 917 | margin-bottom: 20px; 918 | } 919 | 920 | .mobileView { 921 | text-align: left !important; 922 | padding-left: 0 !important; 923 | } 924 | .searchWrapper { 925 | padding: 0px 0; 926 | padding-top: 20px; 927 | position: absolute; 928 | bottom: 0px; 929 | width: calc(100% - 30px); 930 | } 931 | .hitWrapper { 932 | width: 100%; 933 | right: 0; 934 | top: 35px; 935 | max-height: fit-content; 936 | position: static; 937 | } 938 | } 939 | 940 | @media (min-width: 768px) and (max-width: 991px) { 941 | .navBarDefault { 942 | padding: 10px; 943 | } 944 | .navBarBrand { 945 | font-size: 22px; 946 | } 947 | .navBarHeader { 948 | min-width: 240px; 949 | flex: initial; 950 | } 951 | .githubBtn { 952 | padding: 10px 10px; 953 | } 954 | .divider { 955 | margin: 0 5px; 956 | height: 20px; 957 | } 958 | .hitWrapper { 959 | max-width: 500px; 960 | } 961 | .navBarUL li a { 962 | padding: 10px 5px; 963 | } 964 | .searchWrapper { 965 | padding-left: 0px; 966 | } 967 | } 968 | -------------------------------------------------------------------------------- /src/components/theme.js: -------------------------------------------------------------------------------- 1 | export default { 2 | fonts: { 3 | mono: '"SF Mono", "Roboto Mono", Menlo, monospace' 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/components/themeProvider.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { ThemeProvider as EmotionThemeProvider } from 'emotion-theming' 3 | 4 | import { default as defaultTheme } from './theme' 5 | 6 | import Header from './Header' 7 | 8 | import './styles.css' 9 | 10 | export default function ThemeProvider({ children, theme = {}, location }) { 11 | return ( 12 |
      13 | 14 |
      15 | {children} 16 | 17 |
      18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /src/custom-sw-code.js: -------------------------------------------------------------------------------- 1 | workbox.routing.registerRoute( 2 | new RegExp('https:.*min.(css|js)'), 3 | workbox.strategies.staleWhileRevalidate({ 4 | cacheName: 'cdn-cache' 5 | }) 6 | ) 7 | -------------------------------------------------------------------------------- /src/html.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import config from '../config' 5 | 6 | export default class HTML extends React.Component { 7 | render() { 8 | return ( 9 | 10 | 11 | 12 | 13 | 17 | {config.siteMetadata.ogImage ? ( 18 | 19 | ) : null} 20 | 21 | {config.siteMetadata.ogImage ? ( 22 | 26 | ) : null} 27 | {config.siteMetadata.favicon ? ( 28 | 33 | ) : null} 34 | 35 | {this.props.headComponents} 36 | 37 | 38 | {this.props.preBodyComponents} 39 |
      44 | {this.props.postBodyComponents} 45 |