Welcome! This is my personal blog about Web technologies, software development, open source and other related topics
The ideas and opinions expressed here are solely mine and don't represent those of others, either individuals or companies.The code snippets or references to software products or analogous are to be used without any warranty of any kind. If you enjoy the content, feel free to share it and re-use it as long as you provide a link to the original post.
GraphQL delivery API is used to access the published site content. Experience Edge for Experience Manager (XM) is an API-based service from Sitecore that gives you globally replicated, scalable access to your XM Cloud items, layout, and media. You can use the standard publish tools in XM Cloud , but instead of rendering content from a self-hosted Content Delivery environment, Experience Edge provides you a Sitecore-hosted GraphQL API.
To access the XM Cloud hosted published site content using Expereince Edge follow these steps-
Login to XM Cloud. Navigate to Projects ==> Environments ==>Details tab.
impl Description for Language{
fn represent(&self) -> String{
todo!();
//format!("the language {} was lauched in {} year and current stable version is {}", self.name, self.launched_year, self.stable_version)
}
}
Output
Rust
todo!() is actually the same as another macro: unimplemented!(). Programmers were using unimplemented!() a lot but it was long to type, so they created todo!() which is shorter.
No dummy variable needed
Visual “not complete” indicator
Will panic if reached
Panic if reached – if we call the unimplemented trait it will panic and give error.
This macro is kind of like todo!() except it’s for code that you will never do. Maybe you have a match in an enum that you know will never choose one of the arms, so the code can never be reached. If that’s so, you can write unreachable!() so the compiler knows that it can ignore that part.
unreachable!() is also nice for you to read because it reminds you that some part of the code is unreachable. You have to be sure that the code is actually unreachable though. If the compiler ever calls unreachable!(), the program will panic.
dbg! macro
dbg! is a very useful macro that prints quick information. It is a good alternative to println! because it is faster to type and gives more information
As per the Rust documentation, the dbg! macro works exactly the same in release builds. This is useful when debugging issues that only occur in release builds or when debugging in release mode is significantly faster.
[src\main.rs:33:5] &pro_langauge_rust = Language {
name: "Rust",
stable_version: 1.84,
launched_year: 2012,
}
[src\main.rs:34:5] pro_langauge_rust.represent() = "the language Rust was lauched in 2012 year and current stable version is 1.84"
env! and option_env! macro
Any sensitive information that are harcoded can be a security breach. To avoid this these should be go set as environment variables. For this use env! or option_env!
env!(“key”) or option_env!(“key”)
env! will panic if the key does not exists. It is better to use env! if you want program to crash when the environment variable is not found.
option_env! – on the other hand is safer as it won’t panic if the key not found, instead allows to handle the error.
let language_name = env!("name");
This will not compile if the environment variable is not set in project. To do this you can use dotenc crate to read variables from .env file.
But what when the variables are set through command line. This won’t even compile to run the project. Use option_env! macro
let language_name = option_env!("name").expect("Language name is required!");
Make Struct Field optional using Option Enum
Use Option to make the struct field optional and set it to None if it has to be empty or Some if the value has to be set.
Release profile – runs fast but compiles slow as it executes all of the compile optimisations.
cargom run --release
fold() method
fold is a consuming iterator adaptor which applies a function to each element of the iteration, accumulating the result into a new value.
This method is used a lot to add together the items in an iterator, but you can also do a lot more. It is somewhat similar to .for_each(). In .fold(), you first add a starting value (if you are adding items together, then 0), then a comma, then the closure. The closure gives you two items: the total so far, and the next item. First here is a simple example showing .fold() to add items together.
Use generics to create definitions for items like function signatures or structs, which we can then use with many different concrete data types.Ā Defined with <T>
Generics make programming easier without reducing runtime performance.
Generics uses process called Monomorphization where compiler replaces generic placeholders with data types.
struct Language<T, T1, T2>{
name: T,
stable_version: T1,
launched_year: T2
}
fn main{
let pro_langauge_rust = Language{
name: String::from("Rust"),
stable_version: 1.84,
launched_year: 2012
};
println!("Programming language is {:?}", pro_langauge_rust)
}
Output-
Programming language is Language { name: "Rust", stable_version: 1.84, launched_year: 2012 }
A trait defines the functionality a particular type has and can share with other types. We can use traits to define shared behavior in an abstract way. We can use trait bounds to specify that a generic type can be any type that has certain behavior.
Traits are similar to a feature often calledĀ interfacesĀ in other languages, although with some differences.
Struct
Define a struct – in this case Langauge struct with fields and data types
Define trait Description with represent method. Implement trait Description for Language struct.
trait Description{
fn represent(&self) -> String;
}
impl Description for Language{
fn represent(&self) -> String{
format!("the language {} was lauched in {} year and current stable version is {}", self.name, self.launched_year, self.stable_version)
}
}
Main Block
Instantiate Langauge and call the represent method implemented for Struct
{
let pro_langauge_rust = Language{
name: String::from("Rust"),
stable_version: 1.84,
launched_year: 2012
};
println!("Programming language is {}", pro_langauge_rust.represent())
}
Output
Programming language is the language Rust was lauched in 2012 year and current stable version is 1.84
Default Trait
In the previous example Trait just has the definition but no implementation. Define a default implementation to the trait and no remove the implementaion for Langauge
trait Description{
fn represent(&self) -> String{
String::from("is the langauge you should learn.")
}
}
impl Description for Language{
// No implementation
}
fn main{
let pro_langauge_rust = Language{
name: String::from("Rust"),
stable_version: 1.84,
launched_year: 2012
};
println!("{} {}", pro_langauge_rust.name, pro_langauge_rust.represent());
}
This should be the GraphQL Delivery token i.e. GQL token-
There are different ways to generate GQL Token. One of the way is generate from portal. Login to XM Cloud and navigate to Projects ==> Environments ==> Details tab
Click on Generate Delivery API Key (Note this key as this won’t be avilable again and needs to be re-generated again if lost)
Note this API key as this is required later whilst configuring the Vercel hosting.
Publish Site–
Publish the site to Edge before starting to configure Project in Vercel, since we are configuring GraphQL Delivery API token items in Sitecore needs to be published
Create a Project in Vercel
Login to Vercel and Create Project-
Since I have logged in using GitHub account it displays the repositories in Github the project will be based on-
Import the repository-
Configure Project
Once the project is created configure the project by selecting the Framework and repo folder FE code exists-
Choose the Nextjs Framework
Choose the sxastarter folder from the repo i.e. /src/sxastarter
Add following environment variables extracted earlier-
Struct group multiple items of mixed data types. Elements are named instead of order as defined in tuple.
Defining struct
struct Language{
name: String,
stable_version: f32,
launched_year: i32
}
{
let mut pro_langauge = Language{
name: String::from("Rust"),
stable_version: 1.84,
launched_year: 2012
};
println!("Programming language {} current version is {} and launched on {}",
pro_langauge.name,
pro_langauge.stable_version,
pro_langauge.launched_year);
pro_langauge.name = String::from("java");
println!("Programming language is {:?}", pro_langauge);
}
Output
Programming language Rust current version is 1.84 and launched on 2012
Programming language is Language { name: "java", stable_version: 1.84, launched_year: 2012 }
Struct managed in memory
Struct Clone
Struct clone is possible using clone() method. Will take the above example to clone the struct instance.
let pro_langauge_rust = Language{
name: String::from("Rust"),
stable_version: 1.84,
launched_year: 2012
};
let pro_langauge_java = Language{
name: String::from("Java"),
..pro_langauge_rust
};
println!("Programming language {} current version is {} and launched on {}",
pro_langauge_rust.name,
pro_langauge_rust.stable_version,
pro_langauge_rust.launched_year);
println!("Programming language is {:?}", pro_langauge_java);
Here some of the fields are copied from the other instance using .. notation and the name for the new instance is different.
Output
Programming language Rust current version is 1.84 and launched on 2012
Programming language is Language { name: "Java", stable_version: 1.84, launched_year: 2012 }
You cannot copy eveything from the previous instance to the new instance as this breaks the rule oif Rust of having a single variable with same data. For this a clone() method is used.
let pro_langauge_rust = Language{
name: String::from("Rust"),
stable_version: 1.84,
launched_year: 2012
};
let pro_langauge_java = Language{
..pro_langauge_rust
};
Here all the data of the variable pro_langauge_rust is trying to copy to pro_langauge_java gives an error.
Use clone method
let pro_langauge_rust = Language{
name: String::from("Rust"),
stable_version: 1.84,
launched_year: 2012
};
let pro_langauge_java = Language{
..pro_langauge_rust.clone()
};
println!("Programming language {} current version is {} and launched on {}",
pro_langauge_rust.name,
pro_langauge_rust.stable_version,
pro_langauge_rust.launched_year);
println!("Programming language is {:?}", pro_langauge_java);
Output
Programming language Rust current version is 1.84 and launched on 2012
Programming language is Language { name: "Rust", stable_version: 1.84, launched_year: 2012 }
Struct Methods
Subroutine associated with a struct and the first parameter is a reference to the struct instance. It is declared same as funciton using fn keyword.
In the below example the struct method is implemeted using imp keyword. The method takes the reference to the instance i.e. self. This method is called on the Struct instance in main block.
// Implementation of Struct
impl Language{
fn get_name(&self) -> &str
{
&self.name
}
}
fn main() {
let pro_langauge_rust = Language{
name: String::from("Rust"),
stable_version: 1.84,
launched_year: 2012
};
let pro_langauge_java = Language{
..pro_langauge_rust.clone()
};
let language_name = pro_langauge_rust.get_name();
println!("Language name is {:?}", language_name);
}
Update the Struct instance
To update make the struct instance mutable with mut keyword. Implement the method to update in Struct implementaion.
See the update_version method and how is called from the main block
impl Language{
fn get_name(&self) -> &str
{
&self.name
}
fn update_version(&mut self, version: f32){
self.stable_version = version;
}
}
let mut pro_langauge_rust = Language{
name: String::from("Rust"),
stable_version: 1.84,
launched_year: 2012
};
let language_name = pro_langauge_rust.get_name();
println!("Language name is {:?}", language_name);
pro_langauge_rust.update_version(1.85);
println!("Latest version {:?}", pro_langauge_rust.stable_version);
Output
Language name is "Rust"
Latest version 1.85
Associated Functions/Constructor
Functions associated with a struct data type. They are similar to methods but functions does not have a self input parameter. So we cannot use an associated function to reference the data from whithin a specific instance of a struct.
Mainly associated functions in Struct are used as a constructor to build a new instance of a Struct. See below example a new function with the name parameter and return the instance of Struct. The instance of Struct is created in main block.
impl Language{
fn get_name(&self) -> &str
{
&self.name
}
fn update_version(&mut self, version: f32){
self.stable_version = version;
}
fn new(name: &str) -> Language{
Language { name: String::from(name), stable_version: 0.0, launched_year: 0000 }
}
}
{
let mut pro_langauge_rust = Language::new("Rust");
let language_name = pro_langauge_rust.get_name();
println!("Language name is {:?}", language_name);
pro_langauge_rust.update_version(1.85);
println!("Latest version {:?}", pro_langauge_rust.stable_version);
}
Output
Language name is "Rust"
Latest version 1.85
Tuple Structs
Tuple Structs – Store a collection of mixed data without named fields but just the datatype of the field. They are distinguished as a unique data type.
struct Color(u8,u8,u8);
{
let color = Color(255,0,0,);
println!("First value in tuple struct is {}", color.0);
}