├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md └── stale.yml ├── .gitignore ├── .travis.yml ├── CERTIFICATE ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples └── example.rs ├── rustfmt.toml ├── src ├── app.rs ├── error.rs ├── lib.rs ├── player.rs ├── summary.rs └── summary_large_image.rs └── tests └── test.rs /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, 10 | education, socio-economic status, nationality, personal appearance, race, 11 | religion, or sexual identity and orientation. 12 | 13 | ## Our Standards 14 | 15 | Examples of behavior that contributes to creating a positive environment 16 | include: 17 | 18 | - Using welcoming and inclusive language 19 | - Being respectful of differing viewpoints and experiences 20 | - Gracefully accepting constructive criticism 21 | - Focusing on what is best for the community 22 | - Showing empathy towards other community members 23 | 24 | Examples of unacceptable behavior by participants include: 25 | 26 | - The use of sexualized language or imagery and unwelcome sexual attention or 27 | advances 28 | - Trolling, insulting/derogatory comments, and personal or political attacks 29 | - Public or private harassment 30 | - Publishing others' private information, such as a physical or electronic 31 | address, without explicit permission 32 | - Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | 36 | ## Our Responsibilities 37 | 38 | Project maintainers are responsible for clarifying the standards of acceptable 39 | behavior and are expected to take appropriate and fair corrective action in 40 | response to any instances of unacceptable behavior. 41 | 42 | Project maintainers have the right and responsibility to remove, edit, or 43 | reject comments, commits, code, wiki edits, issues, and other contributions 44 | that are not aligned to this Code of Conduct, or to ban temporarily or 45 | permanently any contributor for other behaviors that they deem inappropriate, 46 | threatening, offensive, or harmful. 47 | 48 | ## Scope 49 | 50 | This Code of Conduct applies both within project spaces and in public spaces 51 | when an individual is representing the project or its community. Examples of 52 | representing a project or community include using an official project e-mail 53 | address, posting via an official social media account, or acting as an appointed 54 | representative at an online or offline event. Representation of a project may be 55 | further defined and clarified by project maintainers. 56 | 57 | ## Enforcement 58 | 59 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 60 | reported by contacting the project team at yoshuawuyts@gmail.com, or through 61 | IRC. All complaints will be reviewed and investigated and will result in a 62 | response that is deemed necessary and appropriate to the circumstances. The 63 | project team is obligated to maintain confidentiality with regard to the 64 | reporter of an incident. 65 | Further details of specific enforcement policies may be posted separately. 66 | 67 | Project maintainers who do not follow or enforce the Code of Conduct in good 68 | faith may face temporary or permanent repercussions as determined by other 69 | members of the project's leadership. 70 | 71 | ## Attribution 72 | 73 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 74 | available at 75 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 76 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Contributions include code, documentation, answering user questions, running the 3 | project's infrastructure, and advocating for all types of users. 4 | 5 | The project welcomes all contributions from anyone willing to work in good faith 6 | with other contributors and the community. No contribution is too small and all 7 | contributions are valued. 8 | 9 | This guide explains the process for contributing to the project's GitHub 10 | Repository. 11 | 12 | - [Code of Conduct](#code-of-conduct) 13 | - [Bad Actors](#bad-actors) 14 | - [Developer Certificate of Origin](#developer-certificate-of-origin) 15 | 16 | ## Code of Conduct 17 | The project has a [Code of Conduct](./CODE_OF_CONDUCT.md) that *all* 18 | contributors are expected to follow. This code describes the *minimum* behavior 19 | expectations for all contributors. 20 | 21 | As a contributor, how you choose to act and interact towards your 22 | fellow contributors, as well as to the community, will reflect back not only 23 | on yourself but on the project as a whole. The Code of Conduct is designed and 24 | intended, above all else, to help establish a culture within the project that 25 | allows anyone and everyone who wants to contribute to feel safe doing so. 26 | 27 | Should any individual act in any way that is considered in violation of the 28 | [Code of Conduct](./CODE_OF_CONDUCT.md), corrective actions will be taken. It is 29 | possible, however, for any individual to *act* in such a manner that is not in 30 | violation of the strict letter of the Code of Conduct guidelines while still 31 | going completely against the spirit of what that Code is intended to accomplish. 32 | 33 | Open, diverse, and inclusive communities live and die on the basis of trust. 34 | Contributors can disagree with one another so long as they trust that those 35 | disagreements are in good faith and everyone is working towards a common 36 | goal. 37 | 38 | ## Bad Actors 39 | All contributors to tacitly agree to abide by both the letter and 40 | spirit of the [Code of Conduct](./CODE_OF_CONDUCT.md). Failure, or 41 | unwillingness, to do so will result in contributions being respectfully 42 | declined. 43 | 44 | A *bad actor* is someone who repeatedly violates the *spirit* of the Code of 45 | Conduct through consistent failure to self-regulate the way in which they 46 | interact with other contributors in the project. In doing so, bad actors 47 | alienate other contributors, discourage collaboration, and generally reflect 48 | poorly on the project as a whole. 49 | 50 | Being a bad actor may be intentional or unintentional. Typically, unintentional 51 | bad behavior can be easily corrected by being quick to apologize and correct 52 | course *even if you are not entirely convinced you need to*. Giving other 53 | contributors the benefit of the doubt and having a sincere willingness to admit 54 | that you *might* be wrong is critical for any successful open collaboration. 55 | 56 | Don't be a bad actor. 57 | 58 | ## Developer Certificate of Origin 59 | All contributors must read and agree to the [Developer Certificate of 60 | Origin (DCO)](../CERTIFICATE). 61 | 62 | The DCO allows us to accept contributions from people to the project, similarly 63 | to how a license allows us to distribute our code. 64 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | Explain what is going on. 3 | 4 | ## Your Environment 5 | | Software | Version(s) | 6 | | ---------------- | ---------- | 7 | | twitter-card | 8 | | Rustc | 9 | | Operating System | 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Did something not work as expected? 4 | --- 5 | 6 | # Bug Report 7 | ## Your Environment 8 | | Software | Version(s) | 9 | | ---------------- | ---------- | 10 | | twitter-card | 11 | | Rustc | 12 | | Operating System | 13 | 14 | ## Expected Behavior 15 | Tell us what should have happened. 16 | 17 | ## Current Behavior 18 | Tell us what happens instead of the expected behavior. If you are seeing an 19 | error, please include the full error message and stack trace. 20 | 21 | ## Code Sample 22 | Please provide a code repository, gist, code snippet or sample files to 23 | reproduce the issue. 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Want us to add something to twitter-card? 4 | --- 5 | 6 | # Feature Request 7 | ## Summary 8 | One paragraph explanation of the feature. 9 | 10 | ## Motivation 11 | Why are we doing this? What use cases does it support? What is the expected 12 | outcome? 13 | 14 | ## Guide-level explanation 15 | Explain the proposal as if it was already included in the project and you 16 | were teaching it to another programmer. That generally means: 17 | 18 | - Introducing new named concepts. 19 | - Explaining the feature largely in terms of examples. 20 | - If applicable, provide sample error messages, deprecation warnings, or 21 | migration guidance. 22 | 23 | ## Reference-level explanation 24 | This is the technical portion of the feature request. Explain the design in 25 | sufficient detail that: 26 | 27 | - Its interaction with other features is clear. 28 | - It is reasonably clear how the feature would be implemented. 29 | - Corner cases are dissected by example. 30 | 31 | ## Drawbacks 32 | Why should we _not_ do this? 33 | 34 | ## Rationale and alternatives 35 | - Why is this design the best in the space of possible designs? 36 | - What other designs have been considered and what is the rationale for not 37 | choosing them? 38 | - What is the impact of not doing this? 39 | 40 | ## Unresolved Questions 41 | What related issues do you consider out of scope for this feature that could be 42 | addressed in the future independently of the solution that comes out of this 43 | feature? 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Have any questions regarding how twitter-card works? 4 | --- 5 | 6 | # Question 7 | ## Your Environment 8 | | Software | Version(s) | 9 | | ---------------- | ---------- | 10 | | twitter-card | 11 | | Rustc | 12 | | Operating System | 13 | 14 | ## Question 15 | Provide your question here. 16 | 17 | ## Context 18 | How has this issue affected you? What are you trying to accomplish? 19 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | **Choose one:** is this a 🐛 bug fix, a 🙋 feature, or a 🔦 documentation change? 8 | 9 | 10 | 11 | ## Checklist 12 | 13 | - [ ] tests pass 14 | - [ ] tests and/or benchmarks are included 15 | - [ ] documentation is changed or added 16 | 17 | ## Context 18 | 19 | 20 | ## Semver Changes 21 | 22 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | daysUntilStale: 90 4 | daysUntilClose: 7 5 | exemptLabels: 6 | - pinned 7 | - security 8 | exemptProjects: false 9 | exemptMilestones: false 10 | staleLabel: wontfix 11 | markComment: > 12 | This issue has been automatically marked as stale because it has not had 13 | recent activity. It will be closed if no further activity occurs. Thank you 14 | for your contributions. 15 | unmarkComment: false 16 | closeComment: false 17 | limitPerRun: 30 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | target/ 3 | tmp/ 4 | dist/ 5 | npm-debug.log* 6 | Cargo.lock 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | 5 | before_script: | 6 | rustup component add rustfmt-preview && 7 | rustup component add clippy-preview 8 | script: | 9 | cargo fmt -- --check && 10 | cargo clippy -- -D clippy && 11 | cargo build --verbose && 12 | cargo test --verbose 13 | cache: cargo 14 | -------------------------------------------------------------------------------- /CERTIFICATE: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 1 Letterman Drive 6 | Suite D4700 7 | San Francisco, CA, 94129 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this 10 | license document, but changing it is not allowed. 11 | 12 | 13 | Developer's Certificate of Origin 1.1 14 | 15 | By making a contribution to this project, I certify that: 16 | 17 | (a) The contribution was created in whole or in part by me and I 18 | have the right to submit it under the open source license 19 | indicated in the file; or 20 | 21 | (b) The contribution is based upon previous work that, to the best 22 | of my knowledge, is covered under an appropriate open source 23 | license and I have the right under that license to submit that 24 | work with modifications, whether created in whole or in part 25 | by me, under the same open source license (unless I am 26 | permitted to submit under a different license), as indicated 27 | in the file; or 28 | 29 | (c) The contribution was provided directly to me by some other 30 | person who certified (a), (b) or (c) and I have not modified 31 | it. 32 | 33 | (d) I understand and agree that this project and the contribution 34 | are public and that a record of the contribution (including all 35 | personal information I submit with it, including my sign-off) is 36 | maintained indefinitely and may be redistributed consistent with 37 | this project or the open source license(s) involved. 38 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2018-11-18, Version 1.0.1 2 | ### Commits 3 | - [[`b04dd4aefb`](https://github.com/rust-net-web/twitter-card/commit/b04dd4aefb3eebc4a5cfa65f0367862b38611f2c)] (cargo-release) version 1.0.1 (Yoshua Wuyts) 4 | - [[`64667356f4`](https://github.com/rust-net-web/twitter-card/commit/64667356f4e65a57fe508e0d65e8768e4d85f7bb)] fix missing apis (Yoshua Wuyts) 5 | - [[`040b4a0c97`](https://github.com/rust-net-web/twitter-card/commit/040b4a0c97f04655da13c15b6c0a274979cf9c5a)] cargo fmt (Yoshua Wuyts) 6 | - [[`616ca0c980`](https://github.com/rust-net-web/twitter-card/commit/616ca0c9808cbb0369dd9f3c5c1a2b187ac43580)] Fix typo (#1) (Jan-Erik Rediger) 7 | - [[`8989f074b9`](https://github.com/rust-net-web/twitter-card/commit/8989f074b9768bbd0f348e126e493edb424a5ec1)] Update changelog (Yoshua Wuyts) 8 | 9 | ### Stats 10 | ```diff 11 | CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ 12 | Cargo.toml | 2 +- 13 | src/app.rs | 2 +- 14 | src/lib.rs | 6 ++++++ 15 | src/player.rs | 6 +++--- 16 | tests/test.rs | 2 +- 17 | 6 files changed, 50 insertions(+), 6 deletions(-) 18 | ``` 19 | 20 | 21 | ## 2018-11-17, Version 1.0.0 22 | ### Commits 23 | - [[`62da38ba71`](https://github.com/rust-net-web/twitter-card/commit/62da38ba71a679a5ab799216aa66a25cc97595a5)] (cargo-release) version 1.0.0 (Yoshua Wuyts) 24 | - [[`19eabf0fae`](https://github.com/rust-net-web/twitter-card/commit/19eabf0faefd25edfb81ba622793907975aa54f7)] app card + finalize (Yoshua Wuyts) 25 | - [[`cb8cb958fe`](https://github.com/rust-net-web/twitter-card/commit/cb8cb958feed5994370896b61233d0eae3d4a5ff)] player (Yoshua Wuyts) 26 | - [[`5a9a45993e`](https://github.com/rust-net-web/twitter-card/commit/5a9a45993e2d3b3dfe00df38a518f6db66fb57d4)] summary large image (Yoshua Wuyts) 27 | - [[`7ac8b97875`](https://github.com/rust-net-web/twitter-card/commit/7ac8b97875c766b8d858c696bace36815fd70709)] init (Yoshua Wuyts) 28 | 29 | ### Stats 30 | ```diff 31 | .github/CODE_OF_CONDUCT.md | 75 ++++++++- 32 | .github/CONTRIBUTING.md | 63 ++++++- 33 | .github/ISSUE_TEMPLATE.md | 9 +- 34 | .github/ISSUE_TEMPLATE/bug_report.md | 23 ++- 35 | .github/ISSUE_TEMPLATE/feature_request.md | 43 ++++- 36 | .github/ISSUE_TEMPLATE/question.md | 18 ++- 37 | .github/PULL_REQUEST_TEMPLATE.md | 21 ++- 38 | .github/stale.yml | 17 ++- 39 | .gitignore | 7 +- 40 | .travis.yml | 13 +- 41 | CERTIFICATE | 37 ++++- 42 | Cargo.toml | 16 ++- 43 | LICENSE-APACHE | 190 ++++++++++++++++++++- 44 | LICENSE-MIT | 21 ++- 45 | README.md | 68 +++++++- 46 | examples/example.rs | 14 +- 47 | rustfmt.toml | 2 +- 48 | src/app.rs | 121 ++++++++++++- 49 | src/error.rs | 73 ++++++++- 50 | src/lib.rs | 301 +++++++++++++++++++++++++++++++- 51 | src/player.rs | 118 ++++++++++++- 52 | src/summary.rs | 85 +++++++++- 53 | src/summary_large_image.rs | 92 +++++++++- 54 | tests/test.rs | 9 +- 55 | 24 files changed, 1436 insertions(+) 56 | ``` 57 | 58 | 59 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "twitter-card" 3 | version = "1.0.1" 4 | license = "MIT OR Apache-2.0" 5 | repository = "https://github.com/rust-net-web/twitter-card" 6 | documentation = "https://docs.rs/twitter-card" 7 | description = "Generate HTML for Twitter Card integration." 8 | keywords = ["twitter", "card", "opengraph", "social", "meta"] 9 | categories = ["template-engine", "value-formatting", "web-programming", "web-programming::http-server"] 10 | authors = ["Yoshua Wuyts "] 11 | readme = "README.md" 12 | 13 | [dependencies] 14 | failure = "0.1.3" 15 | 16 | [dev-dependencies] 17 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2018 Yoshua Wuyts 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Yoshua Wuyts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # twitter-card 2 | [![crates.io version][1]][2] [![build status][3]][4] 3 | [![downloads][5]][6] [![docs.rs docs][7]][8] 4 | 5 | Generate HTML for Twitter Card integration. 6 | 7 | - [Documentation][8] 8 | - [Crates.io][2] 9 | - [Releases][releases] 10 | 11 | ## Examples 12 | __Basic usage__ 13 | ```rust 14 | use twitter_card::{Summary, TwitterCard}; 15 | 16 | let card = Summary::builder() 17 | .site("@flickr") 18 | .title("Small Island Developing States Photo Submission") 19 | .desc("View the album on Flickr.") 20 | .image("https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg") 21 | .build(); 22 | ``` 23 | 24 | ```html 25 | <--! Output --> 26 | 27 | 28 | 29 | 30 | 31 | ``` 32 | 33 | ## Installation 34 | ```sh 35 | $ cargo add twitter-card 36 | ``` 37 | 38 | ## Safety 39 | This crate uses ``#![deny(unsafe_code)]`` to ensure everything is implemented in 40 | 100% Safe Rust. 41 | 42 | ## Contributing 43 | Want to join us? Check out our ["Contributing" guide][contributing] and take a 44 | look at some of these issues: 45 | 46 | - [Issues labeled "good first issue"][good-first-issue] 47 | - [Issues labeled "help wanted"][help-wanted] 48 | 49 | ## References 50 | - [Twitter Card Validator](https://cards-dev.twitter.com/validator) 51 | - [Twitter Card Overview](https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/markup) 52 | 53 | ## License 54 | [MIT](./LICENSE-MIT) OR [Apache-2.0](./LICENSE-APACHE) 55 | 56 | [1]: https://img.shields.io/crates/v/twitter-card.svg?style=flat-square 57 | [2]: https://crates.io/crates/twitter-card 58 | [3]: https://img.shields.io/travis/http-rs/twitter-card/main.svg?style=flat-square 59 | [4]: https://travis-ci.org/http-rs/twitter-card 60 | [5]: https://img.shields.io/crates/d/twitter-card.svg?style=flat-square 61 | [6]: https://crates.io/crates/twitter-card 62 | [7]: https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square 63 | [8]: https://docs.rs/twitter-card 64 | 65 | [releases]: https://github.com/http-rs/twitter-card/releases 66 | [contributing]: https://github.com/http-rs/twitter-card/blob/main.github/CONTRIBUTING.md 67 | [good-first-issue]: https://github.com/http-rs/twitter-card/labels/good%20first%20issue 68 | [help-wanted]: https://github.com/http-rs/twitter-card/labels/help%20wanted 69 | -------------------------------------------------------------------------------- /examples/example.rs: -------------------------------------------------------------------------------- 1 | extern crate twitter_card; 2 | 3 | use twitter_card::{Summary, TwitterCard}; 4 | 5 | fn main() { 6 | let card = Summary::builder() 7 | .site("@flickr") 8 | .title("Small Island Developing States Photo Submission") 9 | .desc("View the album on Flickr.") 10 | .image("https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg") 11 | .build(); 12 | 13 | println!("{}", card); 14 | } 15 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | tab_spaces = 2 3 | -------------------------------------------------------------------------------- /src/app.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Create an app card. 4 | /// 5 | /// The App Card is a great way to represent mobile applications on Twitter and 6 | /// to drive installs. We designed the App Card to allow for a name, description 7 | /// and icon, and also to highlight attributes such as the rating and the price. 8 | /// 9 | /// [Read more](https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/app-card) 10 | #[derive(Debug, Clone)] 11 | pub struct App { 12 | strings: Vec, 13 | } 14 | 15 | impl App { 16 | /// Create a new instance. 17 | pub fn builder() -> Self { 18 | Self { 19 | strings: vec![create_card("summary")], 20 | } 21 | } 22 | 23 | /// @username of website. Either twitter:site or twitter:site:id is required. 24 | #[inline] 25 | pub fn site(mut self, site: &str) -> Self { 26 | self.strings.push(create_site(site)); 27 | self 28 | } 29 | 30 | /// Same as twitter:site, but the user’s Twitter ID. Either twitter:site or 31 | /// twitter:site:id is required. 32 | #[inline] 33 | pub fn site_id(mut self, content: &str) -> Self { 34 | self.strings.push(create_site_id(content)); 35 | self 36 | } 37 | 38 | /// Description of content. 39 | /// 40 | /// ## Panics. 41 | /// Panics if the description is more than 200 characters. 42 | #[inline] 43 | pub fn desc(mut self, content: &str) -> Self { 44 | self.strings.push(create_desc(content)); 45 | self 46 | } 47 | 48 | /// Your app ID in the iTunes App Store (Note: NOT your bundle ID). 49 | #[inline] 50 | pub fn app_id_iphone(mut self, content: &str) -> Self { 51 | self.strings.push(create_app_id_iphone(content)); 52 | self 53 | } 54 | 55 | /// Name of your iPhone app. 56 | #[inline] 57 | pub fn app_name_iphone(mut self, content: &str) -> Self { 58 | self.strings.push(create_app_name_iphone(content)); 59 | self 60 | } 61 | 62 | /// Your app’s custom URL scheme (you must include ”://” after your scheme 63 | /// name). 64 | #[inline] 65 | pub fn app_url_iphone(mut self, content: &str) -> Self { 66 | self.strings.push(create_app_url_iphone(content)); 67 | self 68 | } 69 | 70 | /// Name of your iPad app. 71 | #[inline] 72 | pub fn app_name_ipad(mut self, content: &str) -> Self { 73 | self.strings.push(create_app_name_ipad(content)); 74 | self 75 | } 76 | 77 | /// Create a Twitter app id iPad tag. 78 | /// 79 | /// Your app ID in the iTunes App Store (Note: NOT your bundle ID). 80 | #[inline] 81 | pub fn app_id_ipad(mut self, content: &str) -> Self { 82 | self.strings.push(create_app_id_ipad(content)); 83 | self 84 | } 85 | 86 | /// Your app’s custom URL scheme (you must include ”://” after your scheme 87 | /// name). 88 | #[inline] 89 | pub fn app_url_ipad(mut self, content: &str) -> Self { 90 | self.strings.push(create_app_url_ipad(content)); 91 | self 92 | } 93 | 94 | /// Name of your Android app. 95 | #[inline] 96 | pub fn app_name_googleplay(mut self, content: &str) -> Self { 97 | self.strings.push(create_app_name_googleplay(content)); 98 | self 99 | } 100 | 101 | /// Your app ID in the Google Play Store. 102 | #[inline] 103 | pub fn app_id_googleplay(mut self, content: &str) -> Self { 104 | self.strings.push(create_app_id_googleplay(content)); 105 | self 106 | } 107 | 108 | /// Your app’s custom URL scheme. 109 | #[inline] 110 | pub fn app_url_googleplay(mut self, content: &str) -> Self { 111 | self.strings.push(create_app_url_googleplay(content)); 112 | self 113 | } 114 | } 115 | 116 | impl TwitterCard for App { 117 | #[inline] 118 | fn build(self) -> String { 119 | self.strings.join("\n") 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use failure::{self, Backtrace, Context, Fail}; 2 | use std::fmt::{self, Display}; 3 | use std::result; 4 | 5 | /// A specialized [`Result`] type for this crate's operations. 6 | /// 7 | /// This is generally used to avoid writing out [Error] directly and 8 | /// is otherwise a direct mapping to [`Result`]. 9 | /// 10 | /// [`Result`]: https://doc.rust-lang.org/nightly/std/result/enum.Result.html 11 | /// [`Error`]: std.struct.Error.html 12 | pub type Result = result::Result; 13 | 14 | /// A list enumerating the categories of errors in this crate. 15 | /// 16 | /// This list is intended to grow over time and it is not recommended to 17 | /// exhaustively match against it. 18 | /// 19 | /// It is used with the [`Error`] struct. 20 | /// 21 | /// [`Error`]: std.struct.Error.html 22 | #[derive(Debug, Fail)] 23 | pub enum ErrorKind { 24 | /// Any error not part of this list. 25 | #[fail(display = "Generic error.")] 26 | Other, 27 | } 28 | 29 | /// A specialized [`Error`] type for this crate's operations. 30 | /// 31 | /// [`Error`]: https://doc.rust-lang.org/nightly/std/error/trait.Error.html 32 | #[derive(Debug)] 33 | pub struct Error { 34 | inner: Context, 35 | } 36 | 37 | impl Error { 38 | /// Access the [`ErrorKind`] member. 39 | /// 40 | /// [`ErrorKind`]: enum.ErrorKind.html 41 | pub fn kind(&self) -> &ErrorKind { 42 | &*self.inner.get_context() 43 | } 44 | } 45 | 46 | impl Fail for Error { 47 | fn cause(&self) -> Option<&dyn Fail> { 48 | self.inner.cause() 49 | } 50 | 51 | fn backtrace(&self) -> Option<&Backtrace> { 52 | self.inner.backtrace() 53 | } 54 | } 55 | 56 | impl Display for Error { 57 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 58 | Display::fmt(&self.inner, f) 59 | } 60 | } 61 | 62 | impl From for Error { 63 | fn from(kind: ErrorKind) -> Error { 64 | let inner = Context::new(kind); 65 | Error { inner } 66 | } 67 | } 68 | 69 | impl From> for Error { 70 | fn from(inner: Context) -> Error { 71 | Error { inner } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code, future_incompatible)] 2 | #![forbid(rust_2018_idioms, rust_2018_compatibility)] 3 | #![deny(missing_debug_implementations, bad_style)] 4 | #![deny(missing_docs)] 5 | #![cfg_attr(test, deny(warnings))] 6 | 7 | //! Generate HTML for Twitter Card integration. 8 | //! 9 | //! ## Example 10 | //! ```rust 11 | //! use twitter_card::{Summary, TwitterCard}; 12 | //! 13 | //! let card = Summary::builder() 14 | //! .site("@flickr") 15 | //! .title("Small Island Developing States Photo Submission") 16 | //! .desc("View the album on Flickr.") 17 | //! .image("https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg") 18 | //! .build(); 19 | //! ``` 20 | //! 21 | //! ```html 22 | //! 23 | //! 24 | //! 25 | //! 26 | //! 27 | //! ``` 28 | 29 | mod app; 30 | mod player; 31 | mod summary; 32 | mod summary_large_image; 33 | 34 | pub use crate::app::App; 35 | pub use crate::player::Player; 36 | pub use crate::summary::Summary; 37 | pub use crate::summary_large_image::SummaryLargeImage; 38 | 39 | /// Twitter Card. 40 | pub trait TwitterCard { 41 | /// Convert the Twitter Card to a string. 42 | fn build(self) -> String; 43 | } 44 | 45 | /// Create a Twitter card tag. 46 | /// 47 | /// The card type. 48 | /// 49 | /// _Used with all cards._ 50 | #[inline] 51 | pub fn create_card(content: &str) -> String { 52 | create("twitter:card", content) 53 | } 54 | 55 | /// Create a Twitter site tag. 56 | /// 57 | /// @username of website. Either twitter:site or twitter:site:id is required. 58 | /// 59 | /// _Used with summary, summary_large_image, app, player cards._ 60 | #[inline] 61 | pub fn create_site(content: &str) -> String { 62 | create("twitter:site", content) 63 | } 64 | 65 | /// Create a Twitter site id tag. 66 | /// 67 | /// Same as twitter:site, but the user’s Twitter ID. Either twitter:site or 68 | /// twitter:site:id is required. 69 | /// 70 | /// _Used with summary, summary_large_image, player cards_ 71 | #[inline] 72 | pub fn create_site_id(content: &str) -> String { 73 | create("twitter:site:id", content) 74 | } 75 | 76 | /// Create a Twitter description tag. 77 | /// 78 | /// Description of content. 79 | /// 80 | /// _Used with summary, summary_large_image, player cards._ 81 | /// 82 | /// ## Panics. 83 | /// Panics if the description is more than 200 characters. 84 | #[inline] 85 | pub fn create_desc(content: &str) -> String { 86 | debug_assert!( 87 | content.len() <= 200, 88 | "Description has a maximum of 200 characters" 89 | ); 90 | create("twitter:description", content) 91 | } 92 | 93 | /// Create a Twitter title tag. 94 | /// 95 | /// Title of content. 96 | /// 97 | /// _Used with summary, summary_large_image, player cards._ 98 | /// 99 | /// ## Panics. 100 | /// Panics if the description is more than 70 characters. 101 | #[inline] 102 | pub fn create_title(content: &str) -> String { 103 | debug_assert!(content.len() <= 70, "Title has a maximum of 70 characters"); 104 | create("twitter:title", content) 105 | } 106 | 107 | /// Create a Twitter image tag. 108 | /// 109 | /// URL of image to use in the card. Images must be less than 5MB in size. JPG, 110 | /// PNG, WEBP and GIF formats are supported. Only the first frame of an animated 111 | /// GIF will be used. SVG is not supported. 112 | /// 113 | /// _Used with summary, summary_large_image, player cards._ 114 | /// 115 | #[inline] 116 | pub fn create_image(content: &str) -> String { 117 | create("twitter:image", content) 118 | } 119 | 120 | /// Create a Twitter image alt tag. 121 | /// 122 | /// A text description of the image conveying the essential nature of an image 123 | /// to users who are visually impaired. Maximum 420 characters. 124 | /// 125 | /// _Used with summary, summary_large_image, player cards._ 126 | /// 127 | /// ## Panics. 128 | /// Panics if the description is more than 420 characters. 129 | #[inline] 130 | pub fn create_image_alt(content: &str) -> String { 131 | debug_assert!( 132 | content.len() <= 420, 133 | "Image alt has a maximum of 420 characters" 134 | ); 135 | create("twitter:image:alt", content) 136 | } 137 | 138 | /// Create a Twitter creator id tag. 139 | /// 140 | /// Twitter user ID of content creator. 141 | /// 142 | /// _Used with summary, summary_large_image cards_ 143 | #[inline] 144 | pub fn create_creator_id(content: &str) -> String { 145 | create("twitter:creator:id", content) 146 | } 147 | 148 | /// Create a Twitter creator tag. 149 | /// 150 | /// @username of content creator. 151 | /// 152 | /// _Used with summary_large_image cards._ 153 | #[inline] 154 | pub fn create_creator(content: &str) -> String { 155 | create("twitter:creator", content) 156 | } 157 | 158 | /// Create a Twitter player tag. 159 | /// 160 | /// HTTPS URL of player iframe 161 | /// 162 | /// _Used with player card_ 163 | /// 164 | #[inline] 165 | pub fn create_player(content: &str) -> String { 166 | create("twitter:player", content) 167 | } 168 | 169 | /// Create a Twitter player width tag. 170 | /// 171 | /// Width of iframe in pixels. 172 | /// 173 | /// _Used with player card_ 174 | /// 175 | #[inline] 176 | pub fn create_player_width(content: &str) -> String { 177 | create("twitter:player:width", content) 178 | } 179 | 180 | /// Create a Twitter player height tag. 181 | /// 182 | /// Height of iframe in pixels. 183 | /// 184 | /// _Used with player card_ 185 | /// 186 | #[inline] 187 | pub fn create_player_height(content: &str) -> String { 188 | create("twitter:player:height", content) 189 | } 190 | 191 | /// Create a Twitter player stream tag. 192 | /// 193 | /// URL to raw video or audio stream. 194 | /// 195 | /// _Used with player card_ 196 | /// 197 | #[inline] 198 | pub fn create_player_stream(content: &str) -> String { 199 | create("twitter:player:stream", content) 200 | } 201 | 202 | /// Create a Twitter app name iPhone tag. 203 | /// 204 | /// Name of your iPhone app. 205 | /// 206 | /// _Used with app card_ 207 | /// 208 | #[inline] 209 | pub fn create_app_name_iphone(content: &str) -> String { 210 | create("twitter:app:name:iphone", content) 211 | } 212 | 213 | /// Create a Twitter app id iPhone tag. 214 | /// 215 | /// Your app ID in the iTunes App Store (Note: NOT your bundle ID). 216 | /// 217 | /// _Used with app card_ 218 | /// 219 | #[inline] 220 | pub fn create_app_id_iphone(content: &str) -> String { 221 | create("twitter:app:id:iphone", content) 222 | } 223 | 224 | /// Create a Twitter app url iPhone tag. 225 | /// 226 | /// Your app’s custom URL scheme (you must include ”://” after your scheme 227 | /// name). 228 | /// 229 | /// _Used with app card_ 230 | /// 231 | #[inline] 232 | pub fn create_app_url_iphone(content: &str) -> String { 233 | create("twitter:app:url:iphone", content) 234 | } 235 | 236 | /// Create a Twitter app name iPad tag. 237 | /// 238 | /// Name of your iPad app. 239 | /// 240 | /// _Used with app card_ 241 | /// 242 | #[inline] 243 | pub fn create_app_name_ipad(content: &str) -> String { 244 | create("twitter:app:name:ipad", content) 245 | } 246 | 247 | /// Create a Twitter app id iPad tag. 248 | /// 249 | /// Your app ID in the iTunes App Store (Note: NOT your bundle ID). 250 | /// 251 | /// _Used with app card_ 252 | /// 253 | #[inline] 254 | pub fn create_app_id_ipad(content: &str) -> String { 255 | create("twitter:app:id:ipad", content) 256 | } 257 | 258 | /// Create a Twitter app url iPad tag. 259 | /// 260 | /// Your app’s custom URL scheme (you must include ”://” after your scheme 261 | /// name). 262 | /// 263 | /// _Used with app card_ 264 | /// 265 | #[inline] 266 | pub fn create_app_url_ipad(content: &str) -> String { 267 | create("twitter:app:url:ipad", content) 268 | } 269 | 270 | /// Create a Twitter app name Google Play tag. 271 | /// 272 | /// Name of your Android app. 273 | /// 274 | /// _Used with app card_ 275 | /// 276 | #[inline] 277 | pub fn create_app_name_googleplay(content: &str) -> String { 278 | create("twitter:app:name:googleplay", content) 279 | } 280 | 281 | /// Create a Twitter app id Google Play tag. 282 | /// 283 | /// Your app ID in the Google Play Store. 284 | /// 285 | /// _Used with app card_ 286 | /// 287 | #[inline] 288 | pub fn create_app_id_googleplay(content: &str) -> String { 289 | create("twitter:app:id:googleplay", content) 290 | } 291 | 292 | /// Create a Twitter app url Google Play tag. 293 | /// 294 | /// Your app’s custom URL scheme. 295 | /// 296 | /// _Used with app card_ 297 | /// 298 | #[inline] 299 | pub fn create_app_url_googleplay(content: &str) -> String { 300 | create("twitter:app:url:googleplay", content) 301 | } 302 | 303 | /// Create an HTML tag 304 | #[inline] 305 | fn create(name: &str, content: &str) -> String { 306 | format!(r#""#, name, content) 307 | } 308 | -------------------------------------------------------------------------------- /src/player.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Create a player card. 4 | /// 5 | /// Player cards allow rich media to be embedded on Twitter. This includes, but 6 | /// is not limited to, audio clips and videos. 7 | /// 8 | /// [Read more](https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/player-card) 9 | #[derive(Debug, Clone)] 10 | pub struct Player { 11 | strings: Vec, 12 | } 13 | 14 | impl Player { 15 | /// Create a new instance. 16 | pub fn builder() -> Self { 17 | Self { 18 | strings: vec![create_card("summary")], 19 | } 20 | } 21 | 22 | /// Title of content. 23 | /// 24 | /// ## Panics. 25 | /// Panics if the description is more than 70 characters. 26 | #[inline] 27 | pub fn title(mut self, content: &str) -> Self { 28 | self.strings.push(create_title(content)); 29 | self 30 | } 31 | 32 | /// @username of website. Either twitter:site or twitter:site:id is required. 33 | #[inline] 34 | pub fn site(mut self, site: &str) -> Self { 35 | self.strings.push(create_site(site)); 36 | self 37 | } 38 | 39 | /// Same as twitter:site, but the user’s Twitter ID. Either twitter:site or 40 | /// twitter:site:id is required. 41 | #[inline] 42 | pub fn site_id(mut self, content: &str) -> Self { 43 | self.strings.push(create_site_id(content)); 44 | self 45 | } 46 | 47 | /// Twitter user ID of content creator. 48 | #[inline] 49 | pub fn creator_id(mut self, content: &str) -> Self { 50 | self.strings.push(create_creator_id(content)); 51 | self 52 | } 53 | 54 | /// Description of content. 55 | /// 56 | /// ## Panics. 57 | /// Panics if the description is more than 200 characters. 58 | #[inline] 59 | pub fn desc(mut self, content: &str) -> Self { 60 | self.strings.push(create_desc(content)); 61 | self 62 | } 63 | 64 | /// URL of image to use in the card. Images must be less than 5MB in size. 65 | /// JPG, PNG, WEBP and GIF formats are supported. Only the first frame of an 66 | /// animated GIF will be used. SVG is not supported. 67 | #[inline] 68 | pub fn image(mut self, content: &str) -> Self { 69 | self.strings.push(create_image(content)); 70 | self 71 | } 72 | 73 | /// A text description of the image conveying the essential nature of an image 74 | /// to users who are visually impaired. Maximum 420 characters. 75 | /// 76 | /// ## Panics. 77 | /// Panics if the description is more than 420 characters. 78 | #[inline] 79 | pub fn image_alt(mut self, content: &str) -> Self { 80 | self.strings.push(create_image_alt(content)); 81 | self 82 | } 83 | 84 | /// HTTPS URL of player iframe 85 | #[inline] 86 | pub fn player(mut self, content: &str) -> Self { 87 | self.strings.push(create_player(content)); 88 | self 89 | } 90 | 91 | /// Width of iframe in pixels. 92 | #[inline] 93 | pub fn player_width(mut self, content: &str) -> Self { 94 | self.strings.push(create_player_width(content)); 95 | self 96 | } 97 | 98 | /// Height of iframe in pixels. 99 | #[inline] 100 | pub fn player_height(mut self, content: &str) -> Self { 101 | self.strings.push(create_player_height(content)); 102 | self 103 | } 104 | 105 | /// URL to raw video or audio stream. 106 | #[inline] 107 | pub fn player_stream(mut self, content: &str) -> Self { 108 | self.strings.push(create_player_stream(content)); 109 | self 110 | } 111 | } 112 | 113 | impl TwitterCard for Player { 114 | #[inline] 115 | fn build(self) -> String { 116 | self.strings.join("\n") 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/summary.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Create a summary card. 4 | #[derive(Debug, Clone)] 5 | pub struct Summary { 6 | strings: Vec, 7 | } 8 | 9 | impl Summary { 10 | /// Create a new instance. 11 | pub fn builder() -> Self { 12 | Self { 13 | strings: vec![create_card("summary")], 14 | } 15 | } 16 | 17 | /// @username of website. Either twitter:site or twitter:site:id is required. 18 | #[inline] 19 | pub fn site(mut self, site: &str) -> Self { 20 | self.strings.push(create_site(site)); 21 | self 22 | } 23 | 24 | /// Same as twitter:site, but the user’s Twitter ID. Either twitter:site or 25 | /// twitter:site:id is required. 26 | #[inline] 27 | pub fn site_id(mut self, content: &str) -> Self { 28 | self.strings.push(create_site_id(content)); 29 | self 30 | } 31 | 32 | /// Twitter user ID of content creator. 33 | #[inline] 34 | pub fn creator_id(mut self, content: &str) -> Self { 35 | self.strings.push(create_creator_id(content)); 36 | self 37 | } 38 | 39 | /// Description of content. 40 | /// 41 | /// ## Panics. 42 | /// Panics if the description is more than 200 characters. 43 | #[inline] 44 | pub fn desc(mut self, content: &str) -> Self { 45 | self.strings.push(create_desc(content)); 46 | self 47 | } 48 | 49 | /// Title of content. 50 | /// 51 | /// ## Panics. 52 | /// Panics if the description is more than 70 characters. 53 | #[inline] 54 | pub fn title(mut self, content: &str) -> Self { 55 | self.strings.push(create_title(content)); 56 | self 57 | } 58 | 59 | /// URL of image to use in the card. Images must be less than 5MB in size. 60 | /// JPG, PNG, WEBP and GIF formats are supported. Only the first frame of an 61 | /// animated GIF will be used. SVG is not supported. 62 | #[inline] 63 | pub fn image(mut self, content: &str) -> Self { 64 | self.strings.push(create_image(content)); 65 | self 66 | } 67 | 68 | /// A text description of the image conveying the essential nature of an image 69 | /// to users who are visually impaired. Maximum 420 characters. 70 | /// 71 | /// ## Panics. 72 | /// Panics if the description is more than 420 characters. 73 | #[inline] 74 | pub fn image_alt(mut self, content: &str) -> Self { 75 | self.strings.push(create_image_alt(content)); 76 | self 77 | } 78 | } 79 | 80 | impl TwitterCard for Summary { 81 | #[inline] 82 | fn build(self) -> String { 83 | self.strings.join("\n") 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/summary_large_image.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Create a summary with large image card. 4 | #[derive(Debug, Clone)] 5 | pub struct SummaryLargeImage { 6 | strings: Vec, 7 | } 8 | 9 | impl SummaryLargeImage { 10 | /// Create a new instance. 11 | pub fn builder() -> Self { 12 | Self { 13 | strings: vec![create_card("summary_large_image")], 14 | } 15 | } 16 | 17 | /// @username of website. Either twitter:site or twitter:site:id is required. 18 | #[inline] 19 | pub fn site(mut self, site: &str) -> Self { 20 | self.strings.push(create_site(site)); 21 | self 22 | } 23 | 24 | /// Same as twitter:site, but the user’s Twitter ID. Either twitter:site or 25 | /// twitter:site:id is required. 26 | #[inline] 27 | pub fn site_id(mut self, content: &str) -> Self { 28 | self.strings.push(create_site_id(content)); 29 | self 30 | } 31 | 32 | /// Twitter user ID of content creator. 33 | #[inline] 34 | pub fn creator_id(mut self, content: &str) -> Self { 35 | self.strings.push(create_creator_id(content)); 36 | self 37 | } 38 | 39 | /// @username of content creator. 40 | #[inline] 41 | pub fn creator(mut self, content: &str) -> Self { 42 | self.strings.push(create_creator(content)); 43 | self 44 | } 45 | 46 | /// Description of content. 47 | /// 48 | /// ## Panics. 49 | /// Panics if the description is more than 200 characters. 50 | #[inline] 51 | pub fn desc(mut self, content: &str) -> Self { 52 | self.strings.push(create_desc(content)); 53 | self 54 | } 55 | 56 | /// Title of content. 57 | /// 58 | /// ## Panics. 59 | /// Panics if the description is more than 70 characters. 60 | #[inline] 61 | pub fn title(mut self, content: &str) -> Self { 62 | self.strings.push(create_title(content)); 63 | self 64 | } 65 | 66 | /// URL of image to use in the card. Images must be less than 5MB in size. 67 | /// JPG, PNG, WEBP and GIF formats are supported. Only the first frame of an 68 | /// animated GIF will be used. SVG is not supported. 69 | #[inline] 70 | pub fn image(mut self, content: &str) -> Self { 71 | self.strings.push(create_image(content)); 72 | self 73 | } 74 | 75 | /// A text description of the image conveying the essential nature of an image 76 | /// to users who are visually impaired. Maximum 420 characters. 77 | /// 78 | /// ## Panics. 79 | /// Panics if the description is more than 420 characters. 80 | #[inline] 81 | pub fn image_alt(mut self, content: &str) -> Self { 82 | self.strings.push(create_image_alt(content)); 83 | self 84 | } 85 | } 86 | 87 | impl TwitterCard for SummaryLargeImage { 88 | #[inline] 89 | fn build(self) -> String { 90 | self.strings.join("\n") 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | extern crate failure; 2 | extern crate twitter_card; 3 | 4 | use failure::Error; 5 | 6 | #[test] 7 | fn should_work() -> Result<(), Error> { 8 | Ok(()) 9 | } 10 | --------------------------------------------------------------------------------